diff options
205 files changed, 5989 insertions, 4729 deletions
diff --git a/.clang-format b/.clang-format index 617c5db417f..798a8c39a35 100644 --- a/.clang-format +++ b/.clang-format @@ -27,6 +27,7 @@ ForEachMacros: - 'foreach_vlib_frame_bitmap_set_bit_index' - 'FOREACH_ARRAY_ELT' - 'RTE_ETH_FOREACH_DEV' + - 'foreach_clib_stack_frame' - 'foreach_vnet_dev_rx_queue_runtime' - 'foreach_vnet_dev_counter' - 'foreach_vnet_dev_port_rx_queue' diff --git a/.gitignore b/.gitignore index 5c707a8364a..563a1157cf4 100644 --- a/.gitignore +++ b/.gitignore @@ -123,6 +123,9 @@ compile_commands.json /extras/hs-test/hs-test /extras/hs-test/http_server /extras/hs-test/.build.ok +/extras/hs-test/.build.cov.ok +/extras/hs-test/.last_hst_ppid +/extras/hs-test/summary/ # ./configure /CMakeFiles diff --git a/MAINTAINERS b/MAINTAINERS index c310e728227..fd456c04a6c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -192,7 +192,7 @@ I: sr M: Pablo Camarillo <pcamaril@cisco.com> M: Ahmed Abdelsalam <ahabdels@cisco.com> F: src/vnet/srv6/ -F: src/vnet/srmpls/ +F: src/plugin/srmpls/ F: src/examples/srv6-sample-localsid/ VNET IPSec @@ -892,3 +892,8 @@ M: vpp-dev Mailing List <vpp-dev@fd.io> C: Missing Maintainer F: * F: */ + +Netmap +I: netmap +M: Tom Jones <thj@freebsd.org> +F: src/plugins/netmap/ @@ -72,7 +72,7 @@ DEB_DEPENDS += debhelper dkms git libtool libapr1-dev dh-python DEB_DEPENDS += libconfuse-dev git-review exuberant-ctags cscope pkg-config DEB_DEPENDS += gcovr lcov chrpath autoconf libnuma-dev DEB_DEPENDS += python3-all python3-setuptools check -DEB_DEPENDS += libffi-dev python3-ply +DEB_DEPENDS += libffi-dev python3-ply libunwind-dev DEB_DEPENDS += cmake ninja-build python3-jsonschema python3-yaml DEB_DEPENDS += python3-venv # ensurepip DEB_DEPENDS += python3-dev python3-pip @@ -86,6 +86,7 @@ DEB_DEPENDS += nasm DEB_DEPENDS += iperf ethtool # for 'make test TEST=vm_vpp_interfaces' DEB_DEPENDS += libpcap-dev DEB_DEPENDS += tshark +DEB_DEPENDS += jq # for extracting test summary from .json report (hs-test) LIBFFI=libffi6 # works on all but 20.04 and debian-testing @@ -244,6 +245,7 @@ help: @echo " build - build debug binaries" @echo " build-release - build release binaries" @echo " build-coverity - build coverity artifacts" + @echo " build-vpp-gcov - build gcov vpp only" @echo " rebuild - wipe and build debug binaries" @echo " rebuild-release - wipe and build release binaries" @echo " run - run debug binary" @@ -251,6 +253,8 @@ help: @echo " debug - run debug binary with debugger" @echo " debug-release - run release binary with debugger" @echo " test - build and run tests" + @echo " test-cov-hs - build and run host stack tests with coverage" + @echo " test-cov-both - build and run python and host stack tests, merge coverage data" @echo " test-help - show help on test framework" @echo " run-vat - run vpp-api-test tool" @echo " pkg-deb - build DEB packages" @@ -267,8 +271,10 @@ help: @echo " checkstyle-commit - check commit message format" @echo " checkstyle-python - check python coding style using 'black' formatter" @echo " checkstyle-api - check api for incompatible changes" + @echo " checkstyle-go - check style of .go source files" @echo " fixstyle - fix coding style" @echo " fixstyle-python - fix python coding style using 'black' formatter" + @echo " fixstyle-go - format .go source files" @echo " doxygen - DEPRECATED - use 'make docs'" @echo " bootstrap-doxygen - DEPRECATED" @echo " wipe-doxygen - DEPRECATED" @@ -277,6 +283,7 @@ help: @echo " json-api-files - (re)-generate json api files" @echo " json-api-files-debug - (re)-generate json api files for debug target" @echo " go-api-files - (re)-generate golang api files" + @echo " cleanup-hst - stops and removes all docker contaiers and namespaces" @echo " docs - Build the Sphinx documentation" @echo " docs-venv - Build the virtual environment for the Sphinx docs" @echo " docs-clean - Remove the generated files from the Sphinx docs" @@ -439,6 +446,10 @@ rebuild: wipe build build-release: $(BR)/.deps.ok $(call make,$(PLATFORM),$(addsuffix -install,$(TARGETS))) +.PHONY: build-vpp-gcov +build-vpp-gcov: + $(call test,vpp_gcov) + .PHONY: wipe-release wipe-release: test-wipe $(BR)/.deps.ok $(call make,$(PLATFORM),$(addsuffix -wipe,$(TARGETS))) @@ -485,6 +496,20 @@ test-cov: $(eval TEST_GCOV=1) $(call test,vpp_gcov,cov) +.PHONY: test-cov-hs +test-cov-hs: + @$(MAKE) -C extras/hs-test build-cov + @$(MAKE) -C extras/hs-test test-cov + +.PHONY: test-cov-both +test-cov-both: + @echo "Running Python, Golang tests and merging coverage reports." + find $(BR) -name '*.gcda' -delete + @$(MAKE) test-cov + find $(BR) -name '*.gcda' -delete + @$(MAKE) test-cov-hs + @$(MAKE) cov-merge + .PHONY: test-cov-build test-cov-build: $(eval CC=gcc) @@ -501,6 +526,14 @@ test-cov-post: $(eval CC=gcc) $(call test,vpp_gcov,cov-post) +.PHONY: cov-merge +cov-merge: + @lcov --add-tracefile $(BR)/test-coverage-merged/coverage-filtered.info \ + -a $(BR)/test-coverage-merged/coverage-filtered1.info -o $(BR)/test-coverage-merged/coverage-merged.info + @genhtml $(BR)/test-coverage-merged/coverage-merged.info \ + --output-directory $(BR)/test-coverage-merged/html + @echo "Code coverage report is in $(BR)/test-coverage-merged/html/index.html" + .PHONY: test-all test-all: $(eval EXTENDED_TESTS=1) @@ -550,7 +583,7 @@ test-shell-cov: .PHONY: test-dep test-dep: - @make -C test test-dep + @$(MAKE) -C test test-dep .PHONY: test-doc test-doc: @@ -710,6 +743,10 @@ json-api-files-debug: go-api-files: json-api-files $(WS_ROOT)/src/tools/vppapigen/generate_go.py $(ARGS) +.PHONY: cleanup-hst +cleanup-hst: + $(MAKE) -C extras/hs-test cleanup-hst + .PHONY: ctags ctags: ctags.files @ctags --totals --tag-relative=yes -L $< @@ -747,8 +784,16 @@ checkstyle-test: checkstyle-python: @$(MAKE) -C test checkstyle-python-all +.PHONY: checkstyle-go +checkstyle-go: + @$(MAKE) -C extras/hs-test checkstyle-go + +.PHONY: fixstyle-go +fixstyle-go: + @$(MAKE) -C extras/hs-test fixstyle-go + .PHONY: checkstyle-all -checkstyle-all: checkstyle-commit checkstyle checkstyle-python docs-spell +checkstyle-all: checkstyle-commit checkstyle checkstyle-python docs-spell checkstyle-go .PHONY: fixstyle fixstyle: diff --git a/build-root/Makefile b/build-root/Makefile index d69a94c1e41..df4434d7629 100644 --- a/build-root/Makefile +++ b/build-root/Makefile @@ -997,7 +997,7 @@ $(PLATFORM_IMAGE_DIR)/ro.img ro-image: $(patsubst %,%-find-source,$(ROOT_PACKAGE | xargs sign $($(PLATFORM)_public_key) \ $($(PLATFORM)_private_key_passphrase) ; \ fi ; \ - : make read-only file system ; \ + : $(MAKE) read-only file system ; \ mksquashfs \ $${tmp_dir} $${ro_image} \ -no-exports -no-progress -no-recovery ; \ diff --git a/build/external/Makefile b/build/external/Makefile index 046dd7484ae..6b5f9058dda 100644 --- a/build/external/Makefile +++ b/build/external/Makefile @@ -21,7 +21,7 @@ INSTALL_DIR ?= $(CURDIR)/_install PKG_VERSION ?= $(shell git describe --abbrev=0 --match 'v[0-9]*' | cut -d- -f1 | cut -dv -f2 | cut -d. -f1,2) PKG_SUFFIX ?= $(shell git log --oneline v$(PKG_VERSION)-rc0.. . | wc -l) SOURCE_DATE_EPOCH ?= $(shell git log -1 --pretty=%ct .) -ifeq ($shell(uname), FreeBSD) +ifeq ($(shell uname), FreeBSD) JOBS := $(shell nproc) else JOBS := $(if $(shell [ -f /proc/cpuinfo ] && head /proc/cpuinfo),\ @@ -94,7 +94,7 @@ build-deb: $(DEV_DEB) install-deb: ifneq ($(INSTALLED_VER),$(DEB_VER)-$(PKG_SUFFIX)) - @make $(DEV_DEB) + @$(MAKE) $(DEV_DEB) @sudo dpkg -i $(DEV_DEB) else @echo "==========================================================" @@ -175,7 +175,7 @@ ifeq ($(INSTALLED_VER)$(INSTALLED_RPM_VER),) $(MAKE) config else ifneq ($(INSTALLED_VER),) - make check-deb + $(MAKE) check-deb endif ifneq ($(INSTALLED_RPM_VER),) $(MAKE) check-rpm diff --git a/build/external/packages/dpdk.mk b/build/external/packages/dpdk.mk index e0a3ef2559b..8ee65faac8c 100644 --- a/build/external/packages/dpdk.mk +++ b/build/external/packages/dpdk.mk @@ -33,8 +33,11 @@ dpdk_tarball_md5sum_21.11 := 58660bbbe9e95abce86e47692b196555 dpdk_tarball_md5sum := $(dpdk_tarball_md5sum_$(dpdk_version)) dpdk_url := $(dpdk_base_url)/$(dpdk_tarball) dpdk_tarball_strip_dirs := 1 +ifeq ($(shell uname), FreeBSD) +dpdk_depends := $(if $(ARCH_X86_64), ipsec-mb) +else dpdk_depends := rdma-core $(if $(ARCH_X86_64), ipsec-mb) - +endif DPDK_MLX_DEFAULT := $(shell if grep -q "rdma=$(rdma-core_version) dpdk=$(dpdk_version)" mlx_rdma_dpdk_matrix.txt; then echo 'y'; else echo 'n'; fi) DPDK_MLX4_PMD ?= $(DPDK_MLX_DEFAULT) DPDK_MLX5_PMD ?= $(DPDK_MLX_DEFAULT) @@ -134,14 +137,19 @@ endif DPDK_DRIVERS_DISABLED := $(shell echo $(DPDK_DRIVERS_DISABLED) | tr -d '\\\t ') DPDK_LIBS_DISABLED := $(shell echo $(DPDK_LIBS_DISABLED) | tr -d '\\\t ') +SED=sed +ifeq ($(shell uname), FreeBSD) +SED=gsed +endif + HASH := \# # post-meson-setup snippet to alter rte_build_config.h define dpdk_config if grep -q RTE_$(1) $(dpdk_src_dir)/config/rte_config.h ; then \ -sed -i -e 's/$(HASH)define RTE_$(1).*/$(HASH)define RTE_$(1) $(DPDK_$(1))/' \ +$(SED) -i -e 's/$(HASH)define RTE_$(1).*/$(HASH)define RTE_$(1) $(DPDK_$(1))/' \ $(dpdk_src_dir)/config/rte_config.h; \ elif grep -q RTE_$(1) $(dpdk_build_dir)/rte_build_config.h ; then \ -sed -i -e 's/$(HASH)define RTE_$(1).*/$(HASH)define RTE_$(1) $(DPDK_$(1))/' \ +$(SED) -i -e 's/$(HASH)define RTE_$(1).*/$(HASH)define RTE_$(1) $(DPDK_$(1))/' \ $(dpdk_build_dir)/rte_build_config.h; \ else \ echo '$(HASH)define RTE_$(1) $(DPDK_$(1))' \ @@ -157,7 +165,7 @@ if [[ "$(DPDK_$(1))" == "y" ]]; then \ >> $(dpdk_build_dir)/rte_build_config.h ; \ fi; \ elif [[ "$(DPDK_$(1))" == "n" ]]; then \ - sed -i '/$(HASH)define RTE_$(1) .*/d' $(dpdk_build_dir)/rte_build_config.h \ + $(SED) -i '/$(HASH)define RTE_$(1) .*/d' $(dpdk_build_dir)/rte_build_config.h \ $(dpdk_src_dir)/config/rte_config.h ; \ fi endef diff --git a/build/external/packages/ipsec-mb.mk b/build/external/packages/ipsec-mb.mk index d38f2e306ea..ffdbcd23fb2 100644 --- a/build/external/packages/ipsec-mb.mk +++ b/build/external/packages/ipsec-mb.mk @@ -32,7 +32,7 @@ define ipsec-mb_config_cmds endef define ipsec-mb_build_cmds - @make -C $(ipsec-mb_src_dir)/lib -j \ + @$(MAKE) -C $(ipsec-mb_src_dir)/lib -j \ SHARED=n \ SAFE_PARAM=n \ SAFE_LOOKUP=n \ diff --git a/build/external/packages/octeon-roc.mk b/build/external/packages/octeon-roc.mk index 62f5d823bdf..98eb29b95aa 100644 --- a/build/external/packages/octeon-roc.mk +++ b/build/external/packages/octeon-roc.mk @@ -2,12 +2,12 @@ # SPDX-License-Identifier: Apache-2.0 # https://spdx.org/licenses/Apache-2.0.html -octeon-roc_version := 0.3 -octeon-roc_tarball := octeon-roc-v$(octeon-roc_version).tar.gz -octeon-roc_tarball_md5sum := e4a16beb76a6c63af1600dd4d1d752b8 +octeon-roc_version := 0.5 +octeon-roc_tarball := v$(octeon-roc_version).tar.gz +octeon-roc_tarball_md5sum := 76bc56c84935da944bbf340fe5283ef0 octeon-roc_tarball_strip_dirs := 1 -octeon-roc_url := https://github.com/MarvellEmbeddedProcessors/marvell-vpp/archive/refs/tags/$(octeon-roc_tarball) +octeon-roc_url := https://github.com/MarvellEmbeddedProcessors/marvell-octeon-roc/archive/refs/tags/$(octeon-roc_tarball) define octeon-roc_config_cmds @true diff --git a/build/external/packages/xdp-tools.mk b/build/external/packages/xdp-tools.mk index b65ae1361f5..b9285971f42 100644 --- a/build/external/packages/xdp-tools.mk +++ b/build/external/packages/xdp-tools.mk @@ -24,15 +24,15 @@ define xdp-tools_config_cmds endef define xdp-tools_build_cmds - @cd ${xdp-tools_src_dir} && make V=1 BUILD_STATIC_ONLY=y > $(xdp-tools_build_log) + @cd ${xdp-tools_src_dir} && $(MAKE) V=1 BUILD_STATIC_ONLY=y > $(xdp-tools_build_log) endef define xdp-tools_install_cmds @rm -f $(xdp-tools_install_log) @cd ${xdp-tools_src_dir} && \ - make -C lib/libbpf/src install V=1 BUILD_STATIC_ONLY=y PREFIX='' DESTDIR='$(xdp-tools_install_dir)' >> $(xdp-tools_install_log) + $(MAKE) -C lib/libbpf/src install V=1 BUILD_STATIC_ONLY=y PREFIX='' DESTDIR='$(xdp-tools_install_dir)' >> $(xdp-tools_install_log) @cd ${xdp-tools_src_dir} && \ - make libxdp_install V=1 BUILD_STATIC_ONLY=y PREFIX='' DESTDIR='$(xdp-tools_install_dir)' >> $(xdp-tools_install_log) + $(MAKE) libxdp_install V=1 BUILD_STATIC_ONLY=y PREFIX='' DESTDIR='$(xdp-tools_install_dir)' >> $(xdp-tools_install_log) endef $(eval $(call package,xdp-tools)) diff --git a/docs/Makefile b/docs/Makefile index 01e8d659bff..9f209357113 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -70,7 +70,7 @@ venv: spell: venv @( \ . ${VENV_DIR}/bin/activate; \ - make -C ${SCRIPTS_DIR} generate && \ + $(MAKE) -C ${SCRIPTS_DIR} generate && \ $(SPHINXBUILD) -b spelling $(SPHINXOPTS) $(BUILDDIR_SRC) $(BUILDDIR_OUT); \ ) @@ -81,7 +81,7 @@ rebuild-spell: clean spell docs: venv @( \ . ${VENV_DIR}/bin/activate; \ - make -C ${SCRIPTS_DIR} generate && \ + $(MAKE) -C ${SCRIPTS_DIR} generate && \ $(SPHINXBUILD) $(SPHINXOPTS) -b html $(BUILDDIR_SRC) $(BUILDDIR_OUT); \ ) @@ -90,7 +90,7 @@ rebuild: clean docs .PHONY: clean clean: - @make -C ${SCRIPTS_DIR} clean + @$(MAKE) -C ${SCRIPTS_DIR} clean .PHONY: build build: docs diff --git a/docs/aboutvpp/releasenotes/index.rst b/docs/aboutvpp/releasenotes/index.rst index adbe8401d27..6fda5152a58 100644 --- a/docs/aboutvpp/releasenotes/index.rst +++ b/docs/aboutvpp/releasenotes/index.rst @@ -9,6 +9,4 @@ Release notes v24.02 v23.10 v23.06 - v23.02 - v22.10.1 past diff --git a/docs/aboutvpp/releasenotes/past.rst b/docs/aboutvpp/releasenotes/past.rst index 87285828eef..8f15bd4da3e 100644 --- a/docs/aboutvpp/releasenotes/past.rst +++ b/docs/aboutvpp/releasenotes/past.rst @@ -6,6 +6,8 @@ Past releases .. toctree:: :maxdepth: 1 + v23.02 + v22.10.1 v22.10 v22.06.1 v22.06 diff --git a/docs/configuration/reference.rst b/docs/configuration/reference.rst index 598e195af13..d288a6d7788 100644 --- a/docs/configuration/reference.rst +++ b/docs/configuration/reference.rst @@ -497,6 +497,9 @@ The buffers Section buffers-per-numa 128000 default data-size 2048 page-size default-hugepage + numa 1 { + buffers 64000 + } } buffers-per-numa number @@ -532,6 +535,31 @@ Set the page size for buffer allocation page-size default page-size default-hugepage +numa <numa index> { .. } +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Settings specific to a single NUMA domain. + +.. code-block:: console + + buffers { + numa 0 { + buffers 32768 + } + } + +buffers <n> +^^^^^^^^^^^^^^^ + +The number of buffers allocated for this specific NUMA domain. +Default is 0, which falls back to the value configured in **buffers-per-numa**. + +.. code-block:: console + + numa 0 { + buffers 32768 + } + The dpdk Section ---------------- diff --git a/docs/developer/corefeatures/sr/sr_mpls.rst b/docs/developer/corefeatures/sr/sr_mpls.rst index d2fe4025326..9e676db4f07 120000 --- a/docs/developer/corefeatures/sr/sr_mpls.rst +++ b/docs/developer/corefeatures/sr/sr_mpls.rst @@ -1 +1 @@ -../../../../src/vnet/srmpls/sr_doc.rst
\ No newline at end of file +../../../../src/plugins/srmpls/sr_doc.rst
\ No newline at end of file diff --git a/docs/developer/plugins/http.rst b/docs/developer/plugins/http.rst new file mode 120000 index 00000000000..b0e49bbb721 --- /dev/null +++ b/docs/developer/plugins/http.rst @@ -0,0 +1 @@ +../../../src/plugins/http/http_plugin.rst
\ No newline at end of file diff --git a/docs/developer/plugins/index.rst b/docs/developer/plugins/index.rst index 91af95f3318..c9081a8caaf 100644 --- a/docs/developer/plugins/index.rst +++ b/docs/developer/plugins/index.rst @@ -42,3 +42,4 @@ For more on plugins please refer to :ref:`add_plugin`. bufmon_doc ip_session_redirect_doc bpf_trace_filter + http diff --git a/docs/gettingstarted/progressivevpp/index.rst b/docs/gettingstarted/progressivevpp/index.rst index 7d1a2e2b237..efe31b12db5 100644 --- a/docs/gettingstarted/progressivevpp/index.rst +++ b/docs/gettingstarted/progressivevpp/index.rst @@ -6,7 +6,7 @@ Progressive VPP Tutorial ######################## -Learn to run FD.io VPP on a single Ubuntu 16.04 VM using Vagrant with this walkthrough +Learn to run FD.io VPP on a single Ubuntu VM using Vagrant with this walkthrough covering basic FD.io VPP scenarios. Useful FD.io VPP commands will be used, and will discuss basic operations, and the state of a running FD.io VPP on a system. diff --git a/docs/gettingstarted/progressivevpp/settingupenvironment.rst b/docs/gettingstarted/progressivevpp/settingupenvironment.rst index 8c67c79b188..a8fe16aa4ce 100644 --- a/docs/gettingstarted/progressivevpp/settingupenvironment.rst +++ b/docs/gettingstarted/progressivevpp/settingupenvironment.rst @@ -3,15 +3,15 @@ Setting up your environment =========================== -All of these exercises are designed to be performed on an Ubuntu 16.04 (Xenial) box. +All of these exercises are designed to be performed on an Ubuntu 22.04 (Jammy) box. -* If you have an Ubuntu 18.04 box on which you have sudo or root access, you can feel free to use that. -* If you do not, a Vagrantfile is provided to setup a basic Ubuntu 18.04 box for you in the the steps below. +* If you have an Ubuntu 22.04 box on which you have sudo or root access, you can feel free to use that. +* If you do not, a Vagrantfile is provided to setup a basic Ubuntu 22.04 box for you in the the steps below. -Install Virtual Box and Vagrant +Install Libvirt and Vagrant ------------------------------- -You will need to install Virtual Box and Vagrant. +You will need to install Libvirt and Vagrant. Create a Vagrant Directory --------------------------- @@ -32,21 +32,16 @@ Create a file called **Vagrantfile** with the following contents: Vagrant.configure(2) do |config| - config.vm.box = "bento/ubuntu-18.04" - config.vm.box_check_update = false + config.vm.box = "generic/ubuntu2204" vmcpu=(ENV['VPP_VAGRANT_VMCPU'] || 2) vmram=(ENV['VPP_VAGRANT_VMRAM'] || 4096) config.ssh.forward_agent = true - config.vm.provider "virtualbox" do |vb| - vb.customize ["modifyvm", :id, "--ioapic", "on"] + config.vm.provider "libvirt" do |vb| vb.memory = "#{vmram}" vb.cpus = "#{vmcpu}" - #support for the SSE4.x instruction is required in some versions of VB. - vb.customize ["setextradata", :id, "VBoxInternal/CPUM/SSE4.1", "1"] - vb.customize ["setextradata", :id, "VBoxInternal/CPUM/SSE4.2", "1"] end end @@ -97,7 +92,7 @@ We write this file with the following contents: .. code-block:: console $ sudo bash - # echo "deb [trusted=yes] https://packagecloud.io/fdio/release/ubuntu bionic main" > /etc/apt/sources.list.d/99fd.io.list + # echo "deb https://packagecloud.io/fdio/release/ubuntu jammy main" > /etc/apt/sources.list.d/99fd.io.list # Get the key. diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index 2348c8dc391..f90ffa89216 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -1047,6 +1047,7 @@ srcIP srcPortDefinition srcUP srh +srmpls Srmpls srtp SRTP diff --git a/extras/hs-test/Makefile b/extras/hs-test/Makefile index d4e699940d1..7fab6dca514 100644 --- a/extras/hs-test/Makefile +++ b/extras/hs-test/Makefile @@ -1,3 +1,9 @@ +export HS_ROOT=$(CURDIR) + +# sets WS_ROOT if called from extras/hs-test +ifeq ($(WS_ROOT),) +export WS_ROOT=$(HS_ROOT)/../.. +endif ifeq ($(VERBOSE),) VERBOSE=false @@ -15,6 +21,10 @@ ifeq ($(TEST),) TEST=all endif +ifeq ($(TEST-HS),) +TEST-HS=all +endif + ifeq ($(DEBUG),) DEBUG=false endif @@ -50,10 +60,14 @@ list_tests = @go run github.com/onsi/ginkgo/v2/ginkgo --dry-run -v --no-color -- help: @echo "Make targets:" @echo " test - run tests" + @echo " test-debug - run tests (vpp debug image)" @echo " build - build test infra" + @echo " build-cov - coverage build of VPP and Docker images" @echo " build-debug - build test infra (vpp debug image)" @echo " build-go - just build golang files" - @echo " fixstyle - format .go source files" + @echo " checkstyle-go - check style of .go source files" + @echo " fixstyle-go - format .go source files" + @echo " cleanup-hst - stops and removes all docker contaiers and namespaces" @echo " list-tests - list all tests" @echo @echo "make build arguments:" @@ -66,7 +80,7 @@ help: @echo " UNCONFIGURE=[true|false] - unconfigure selected test" @echo " DEBUG=[true|false] - attach VPP to GDB" @echo " TEST=[test-name] - specific test to run" - @echo " CPUS=[n-cpus] - number of cpus to run with vpp" + @echo " CPUS=[n-cpus] - number of cpus to allocate to VPP and containers" @echo " VPPSRC=[path-to-vpp-src] - path to vpp source files (for gdb)" @echo " PARALLEL=[n-cpus] - number of test processes to spawn to run in parallel" @echo " REPEAT=[n] - repeat tests up to N times or until a failure occurs" @@ -80,20 +94,50 @@ list-tests: .PHONY: build-vpp-release build-vpp-release: - @make -C ../.. build-release + @$(MAKE) -C ../.. build-release .PHONY: build-vpp-debug build-vpp-debug: - @make -C ../.. build + @$(MAKE) -C ../.. build + +.PHONY: build-vpp-gcov +build-vpp-gcov: + @$(MAKE) -C ../.. build-vpp-gcov .build.ok: build @touch .build.ok +.build.cov.ok: build-vpp-gcov + @touch .build.cov.ok + +.build_debug.ok: build-debug + @touch .build.ok + .PHONY: test test: .deps.ok .build.ok - @bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \ + @# '-' ignores the exit status, it is set in compress.sh + @# necessary so gmake won't skip executing the bash script + @-bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \ + --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \ + --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT) + @bash ./script/compress.sh + +.PHONY: test-debug +test-debug: .deps.ok .build_debug.ok + @# '-' ignores the exit status, it is set in compress.sh + @# necessary so gmake won't skip executing the bash script + @-bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \ --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \ --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT) + @bash ./script/compress.sh + +.PHONY: test-cov +test-cov: .deps.ok .build.cov.ok + @-bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \ + --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST-HS) --cpus=$(CPUS) \ + --vppsrc=$(VPPSRC) + @$(MAKE) -C ../.. test-cov-post HS_TEST=1 + @bash ./script/compress.sh .PHONY: build-go build-go: @@ -105,6 +149,12 @@ build: .deps.ok build-vpp-release build-go bash ./script/build_hst.sh release @touch .build.ok +.PHONY: build-cov +build-cov: .deps.ok build-vpp-gcov build-go + @rm -f .build.cov.ok + bash ./script/build_hst.sh gcov + @touch .build.cov.ok + .PHONY: build-debug build-debug: .deps.ok build-vpp-debug build-go @rm -f .build.ok @@ -112,7 +162,7 @@ build-debug: .deps.ok build-vpp-debug build-go @touch .build.ok .deps.ok: - @sudo make install-deps + @sudo $(MAKE) install-deps .PHONY: install-deps install-deps: @@ -128,7 +178,49 @@ install-deps: fi @touch .deps.ok -.PHONY: fixstyle -fixstyle: - @gofmt -w . +.PHONY: checkstyle-go +checkstyle-go: + @output=$$(gofmt -d $${WS_ROOT}); \ + if [ -z "$$output" ]; then \ + echo "*******************************************************************"; \ + echo "Checkstyle OK."; \ + echo "*******************************************************************"; \ + else \ + echo "$$output"; \ + echo "*******************************************************************"; \ + echo "Checkstyle failed. Use 'make fixstyle-go' or fix errors manually."; \ + echo "*******************************************************************"; \ + exit 1; \ + fi + +.PHONY: fixstyle-go +fixstyle-go: + @echo "Modified files:" + @gofmt -w -l $(WS_ROOT) @go mod tidy + @echo "*******************************************************************" + @echo "Fixstyle done." + @echo "*******************************************************************" + +.PHONY: cleanup-hst +cleanup-hst: + @if [ ! -f ".last_hst_ppid" ]; then \ + echo "'.last_hst_ppid' file does not exist."; \ + exit 1; \ + fi + @echo "****************************" + @echo "Removing docker containers:" + @# "-" ignores errors + @-sudo docker rm $$(sudo docker stop $$(sudo docker ps -a -q --filter "name=$$(cat .last_hst_ppid)") -t 0) + @echo "****************************" + @echo "Removing IP address files:" + @find . -type f -regextype egrep -regex '.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' -exec sudo rm -v {} \; + @echo "****************************" + @echo "Removing network namespaces:" + @for ns in $$(ip netns list | grep $$(cat .last_hst_ppid) | awk '{print $$1}'); do \ + echo $$ns; \ + sudo ip netns delete $$ns; \ + done + @echo "****************************" + @echo "Done." + @echo "****************************" diff --git a/extras/hs-test/README.rst b/extras/hs-test/README.rst index 1dc1039b33f..a88251af617 100644 --- a/extras/hs-test/README.rst +++ b/extras/hs-test/README.rst @@ -89,6 +89,30 @@ This can be put in file ``extras/hs-test/my_test.go`` and run with command ``mak s.log(result) } + +Filtering test cases +-------------------- + +The framework allows us to filter test cases in a few different ways, using ``make test TEST=``: +* Suite name +* File name +* Test name +* All of the above as long as they are ordered properly, e.g. ``make test TEST=VethsSuite.http_test.go.HeaderServerTest`` + +**Names are case sensitive!** + +Names don't have to be complete, as long as they are last: +This is valid and will run all tests in every ``http`` file (if there is more than one): +``make test TEST=VethsSuite.http`` +This is not valid: +``make test TEST=Veths.http`` + +They can also be left out: +``make test TEST=http_test.go`` will run every test in ``http_test.go`` +``make test TEST=Nginx`` will run everything that has 'Nginx' in its name - suites, files and tests. +``make test TEST=HeaderServerTest`` will only run the header server test + + Modifying the framework ----------------------- diff --git a/extras/hs-test/container.go b/extras/hs-test/container.go index c82f1fc03a7..97b71b5385f 100644 --- a/extras/hs-test/container.go +++ b/extras/hs-test/container.go @@ -37,6 +37,7 @@ type Container struct { volumes map[string]Volume envVars map[string]string vppInstance *VppInstance + allocatedCpus []int } func newContainer(suite *HstSuite, yamlInput ContainerConfig) (*Container, error) { @@ -77,7 +78,7 @@ func newContainer(suite *HstSuite, yamlInput ContainerConfig) (*Container, error } if _, ok := yamlInput["volumes"]; ok { - workingVolumeDir := logDir + CurrentSpecReport().LeafNodeText + container.suite.pid + volumeDir + workingVolumeDir := logDir + suite.getCurrentTestName() + volumeDir workDirReplacer := strings.NewReplacer("$HST_DIR", workDir) volDirReplacer := strings.NewReplacer("$HST_VOLUME_DIR", workingVolumeDir) for _, volu := range yamlInput["volumes"].([]interface{}) { @@ -131,7 +132,9 @@ func (c *Container) getContainerWorkDir() (res string) { } func (c *Container) getContainerArguments() string { - args := "--ulimit nofile=90000:90000 --cap-add=all --privileged --network host --rm" + args := "--ulimit nofile=90000:90000 --cap-add=all --privileged --network host" + c.allocateCpus() + args += fmt.Sprintf(" --cpuset-cpus=\"%d-%d\"", c.allocatedCpus[0], c.allocatedCpus[len(c.allocatedCpus)-1]) args += c.getVolumesAsCliOption() args += c.getEnvVarsAsCliOption() if *vppSourceFileDir != "" { @@ -160,6 +163,12 @@ func (c *Container) create() error { return exechelper.Run(cmd) } +func (c *Container) allocateCpus() { + c.suite.startedContainers = append(c.suite.startedContainers, c) + c.allocatedCpus = c.suite.AllocateCpus() + c.suite.log("Allocated CPUs " + fmt.Sprint(c.allocatedCpus) + " to container " + c.name) +} + func (c *Container) start() error { cmd := "docker start " + c.name c.suite.log(cmd) @@ -175,6 +184,7 @@ func (c *Container) prepareCommand() (string, error) { if c.runDetached { cmd += " -d" } + cmd += " " + c.getContainerArguments() c.suite.log(cmd) @@ -250,7 +260,7 @@ func (c *Container) copy(sourceFileName string, targetFileName string) error { } func (c *Container) createFile(destFileName string, content string) error { - f, err := os.CreateTemp("/tmp", "hst-config"+c.suite.pid) + f, err := os.CreateTemp("/tmp", "hst-config"+c.suite.ppid) if err != nil { return err } @@ -286,13 +296,13 @@ func (c *Container) exec(command string, arguments ...any) string { GinkgoHelper() c.suite.log(containerExecCommand) byteOutput, err := exechelper.CombinedOutput(containerExecCommand) - c.suite.assertNil(err, err) + c.suite.assertNil(err, fmt.Sprint(err)) return string(byteOutput) } func (c *Container) getLogDirPath() string { testId := c.suite.getTestId() - testName := CurrentSpecReport().LeafNodeText + testName := c.suite.getCurrentTestName() logDirPath := logDir + testName + "/" + testId + "/" cmd := exec.Command("mkdir", "-p", logDirPath) @@ -304,17 +314,13 @@ func (c *Container) getLogDirPath() string { } func (c *Container) saveLogs() { - cmd := exec.Command("docker", "inspect", "--format='{{.State.Status}}'", c.name) - if output, _ := cmd.CombinedOutput(); !strings.Contains(string(output), "running") { - return - } - testLogFilePath := c.getLogDirPath() + "container-" + c.name + ".log" - cmd = exec.Command("docker", "logs", "--details", "-t", c.name) + cmd := exec.Command("docker", "logs", "--details", "-t", c.name) + c.suite.log(cmd) output, err := cmd.CombinedOutput() if err != nil { - Fail("fetching logs error: " + fmt.Sprint(err)) + c.suite.log(err) } f, err := os.Create(testLogFilePath) @@ -340,12 +346,13 @@ func (c *Container) log(maxLines int) (string, error) { } func (c *Container) stop() error { - if c.vppInstance != nil && c.vppInstance.apiChannel != nil { + if c.vppInstance != nil && c.vppInstance.apiStream != nil { c.vppInstance.saveLogs() c.vppInstance.disconnect() } c.vppInstance = nil c.saveLogs() + c.suite.log("docker stop " + c.name + " -t 0") return exechelper.Run("docker stop " + c.name + " -t 0") } diff --git a/extras/hs-test/cpu.go b/extras/hs-test/cpu.go index a976f47d8a5..49a7dfb02f8 100644 --- a/extras/hs-test/cpu.go +++ b/extras/hs-test/cpu.go @@ -4,6 +4,7 @@ import ( "bufio" "errors" "fmt" + . "github.com/onsi/ginkgo/v2" "os" "os/exec" "strings" @@ -16,26 +17,35 @@ type CpuContext struct { cpus []int } -func (c *CpuContext) Release() { - c.cpuAllocator.cpus = append(c.cpuAllocator.cpus, c.cpus...) - c.cpus = c.cpus[:0] // empty the list -} - type CpuAllocatorT struct { cpus []int } var cpuAllocator *CpuAllocatorT = nil -func (c *CpuAllocatorT) Allocate(nCpus int) (*CpuContext, error) { +func (c *CpuAllocatorT) Allocate(containerCount int, nCpus int) (*CpuContext, error) { var cpuCtx CpuContext - if len(c.cpus) < nCpus { - return nil, fmt.Errorf("could not allocate %d CPUs; available: %d", nCpus, len(c.cpus)) + // splitting cpus into equal parts; this will over-allocate cores but it's good enough for now + maxContainerCount := 4 + // skip CPU 0 + minCpu := ((GinkgoParallelProcess() - 1) * maxContainerCount * nCpus) + 1 + maxCpu := (GinkgoParallelProcess() * maxContainerCount * nCpus) + + if len(c.cpus)-1 < maxCpu { + err := fmt.Errorf("could not allocate %d CPUs; available: %d; attempted to allocate cores %d-%d", + nCpus*containerCount, len(c.cpus)-1, minCpu, maxCpu) + return nil, err } - cpuCtx.cpus = c.cpus[0:nCpus] + if containerCount == 1 { + cpuCtx.cpus = c.cpus[minCpu : minCpu+nCpus] + } else if containerCount > 1 && containerCount <= maxContainerCount { + cpuCtx.cpus = c.cpus[minCpu+(nCpus*(containerCount-1)) : minCpu+(nCpus*containerCount)] + } else { + return nil, fmt.Errorf("too many containers; CPU allocation for >%d containers is not implemented", maxContainerCount) + } + cpuCtx.cpuAllocator = c - c.cpus = c.cpus[nCpus:] return &cpuCtx, nil } diff --git a/extras/hs-test/docker/Dockerfile.nginx b/extras/hs-test/docker/Dockerfile.nginx index 11ec6af156d..386f8e90016 100644 --- a/extras/hs-test/docker/Dockerfile.nginx +++ b/extras/hs-test/docker/Dockerfile.nginx @@ -3,7 +3,7 @@ ARG UBUNTU_VERSION FROM ubuntu:${UBUNTU_VERSION} RUN apt-get update \ - && apt-get install -y nginx gdb less \ + && apt-get install -y nginx gdb less libunwind-dev \ && rm -rf /var/lib/apt/lists/* COPY vpp-data/lib/* /usr/lib/ diff --git a/extras/hs-test/docker/Dockerfile.vpp b/extras/hs-test/docker/Dockerfile.vpp index ace83c593c6..f87ee30c332 100644 --- a/extras/hs-test/docker/Dockerfile.vpp +++ b/extras/hs-test/docker/Dockerfile.vpp @@ -5,7 +5,7 @@ FROM ubuntu:${UBUNTU_VERSION} RUN apt-get update \ && apt-get install -y openssl libapr1 libnuma1 libsubunit0 \ iproute2 libnl-3-dev libnl-route-3-dev python3 iputils-ping \ - vim gdb \ + vim gdb libunwind-dev \ && rm -rf /var/lib/apt/lists/* ENV DIR=vpp-data/lib/vpp_plugins @@ -20,6 +20,7 @@ COPY \ $DIR/nsim_plugin.so \ $DIR/prom_plugin.so \ $DIR/tlsopenssl_plugin.so \ + $DIR/mactime_plugin.so \ /usr/lib/x86_64-linux-gnu/vpp_plugins/ COPY vpp-data/bin/vpp /usr/bin/ diff --git a/extras/hs-test/echo_test.go b/extras/hs-test/echo_test.go index 0515b5e0411..ce852bea3e0 100644 --- a/extras/hs-test/echo_test.go +++ b/extras/hs-test/echo_test.go @@ -21,7 +21,9 @@ func EchoBuiltinTest(s *VethsSuite) { s.assertNotContains(o, "failed:") } +// unstable with multiple workers func TcpWithLossTest(s *VethsSuite) { + s.SkipIfMultiWorker() serverVpp := s.getContainerByName("server-vpp").vppInstance serverVeth := s.getInterfaceByName(serverInterfaceName) @@ -45,5 +47,5 @@ func TcpWithLossTest(s *VethsSuite) { serverVeth.ip4AddressString()) s.log(output) s.assertNotEqual(len(output), 0) - s.assertNotContains(output, "failed: timeout", output) + s.assertNotContains(output, "failed", output) } diff --git a/extras/hs-test/framework_test.go b/extras/hs-test/framework_test.go index 8773fa2417d..b992659a4af 100644 --- a/extras/hs-test/framework_test.go +++ b/extras/hs-test/framework_test.go @@ -1,13 +1,39 @@ package main import ( + "fmt" + "os" + "path/filepath" + "runtime" "testing" + "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) +var suiteTimeout time.Duration + +func getTestFilename() string { + _, filename, _, _ := runtime.Caller(2) + return filepath.Base(filename) +} + func TestHst(t *testing.T) { + if *isVppDebug { + // 30 minute timeout so that the framework won't timeout while debugging + suiteTimeout = time.Minute * 30 + } else { + suiteTimeout = time.Minute * 5 + } + + // creates a file with PPID, used for 'make cleanup-hst' + ppid := fmt.Sprint(os.Getppid()) + ppid = ppid[:len(ppid)-1] + f, _ := os.Create(".last_hst_ppid") + f.Write([]byte(ppid)) + f.Close() + RegisterFailHandler(Fail) RunSpecs(t, "HST") } diff --git a/extras/hs-test/go.mod b/extras/hs-test/go.mod index 0f3854d2148..3be9ba20a86 100644 --- a/extras/hs-test/go.mod +++ b/extras/hs-test/go.mod @@ -26,7 +26,7 @@ require ( github.com/onsi/ginkgo/v2 v2.16.0 github.com/onsi/gomega v1.32.0 github.com/pkg/errors v0.9.1 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sirupsen/logrus v1.9.3 github.com/vishvananda/netns v0.0.4 // indirect golang.org/x/sys v0.16.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/extras/hs-test/hst_suite.go b/extras/hs-test/hst_suite.go index 4c6d5b2664b..0bdaad28ef5 100644 --- a/extras/hs-test/hst_suite.go +++ b/extras/hs-test/hst_suite.go @@ -5,7 +5,10 @@ import ( "errors" "flag" "fmt" - "log/slog" + "github.com/onsi/gomega/gmeasure" + "gopkg.in/yaml.v3" + "io" + "log" "os" "os/exec" "strings" @@ -14,7 +17,6 @@ import ( "github.com/edwarnicke/exechelper" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "gopkg.in/yaml.v3" ) const ( @@ -29,21 +31,34 @@ var nConfiguredCpus = flag.Int("cpus", 1, "number of CPUs assigned to vpp") var vppSourceFileDir = flag.String("vppsrc", "", "vpp source file directory") type HstSuite struct { - containers map[string]*Container - volumes []string - netConfigs []NetConfig - netInterfaces map[string]*NetInterface - ip4AddrAllocator *Ip4AddressAllocator - testIds map[string]string - cpuAllocator *CpuAllocatorT - cpuContexts []*CpuContext - cpuPerVpp int - pid string + containers map[string]*Container + startedContainers []*Container + volumes []string + netConfigs []NetConfig + netInterfaces map[string]*NetInterface + ip4AddrAllocator *Ip4AddressAllocator + testIds map[string]string + cpuAllocator *CpuAllocatorT + cpuContexts []*CpuContext + cpuPerVpp int + ppid string + processIndex string + logger *log.Logger + logFile *os.File } func (s *HstSuite) SetupSuite() { + s.createLogger() + s.log("Suite Setup") + RegisterFailHandler(func(message string, callerSkip ...int) { + s.hstFail() + Fail(message, callerSkip...) + }) var err error - s.pid = fmt.Sprint(os.Getpid()) + s.ppid = fmt.Sprint(os.Getppid()) + // remove last number so we have space to prepend a process index (interfaces have a char limit) + s.ppid = s.ppid[:len(s.ppid)-1] + s.processIndex = fmt.Sprint(GinkgoParallelProcess()) s.cpuAllocator, err = CpuAllocator() if err != nil { Fail("failed to init cpu allocator: " + fmt.Sprint(err)) @@ -52,7 +67,7 @@ func (s *HstSuite) SetupSuite() { } func (s *HstSuite) AllocateCpus() []int { - cpuCtx, err := s.cpuAllocator.Allocate(s.cpuPerVpp) + cpuCtx, err := s.cpuAllocator.Allocate(len(s.startedContainers), s.cpuPerVpp) s.assertNil(err) s.AddCpuContext(cpuCtx) return cpuCtx.cpus @@ -63,16 +78,16 @@ func (s *HstSuite) AddCpuContext(cpuCtx *CpuContext) { } func (s *HstSuite) TearDownSuite() { + defer s.logFile.Close() + s.log("Suite Teardown") s.unconfigureNetworkTopology() } func (s *HstSuite) TearDownTest() { + s.log("Test Teardown") if *isPersistent { return } - for _, c := range s.cpuContexts { - c.Release() - } s.resetContainers() s.removeVolumes() s.ip4AddrAllocator.deleteIpAddresses() @@ -85,10 +100,8 @@ func (s *HstSuite) skipIfUnconfiguring() { } func (s *HstSuite) SetupTest() { - RegisterFailHandler(func(message string, callerSkip ...int) { - s.hstFail() - Fail(message, callerSkip...) - }) + s.log("Test Setup") + s.startedContainers = s.startedContainers[:0] s.skipIfUnconfiguring() s.setupVolumes() s.setupContainers() @@ -110,7 +123,7 @@ func (s *HstSuite) setupContainers() { } } -func logVppInstance(container *Container, maxLines int) { +func (s *HstSuite) logVppInstance(container *Container, maxLines int) { if container.vppInstance == nil { return } @@ -136,26 +149,26 @@ func logVppInstance(container *Container, maxLines int) { } } - fmt.Println("vvvvvvvvvvvvvvv " + container.name + " [VPP instance]:") + s.log("vvvvvvvvvvvvvvv " + container.name + " [VPP instance]:") for _, line := range lines { - fmt.Println(line) + s.log(line) } - fmt.Printf("^^^^^^^^^^^^^^^\n\n") + s.log("^^^^^^^^^^^^^^^\n\n") } func (s *HstSuite) hstFail() { - fmt.Println("Containers: " + fmt.Sprint(s.containers)) - for _, container := range s.containers { + for _, container := range s.startedContainers { out, err := container.log(20) if err != nil { - fmt.Printf("An error occured while obtaining '%s' container logs: %s\n", container.name, fmt.Sprint(err)) - break + s.log("An error occured while obtaining '" + container.name + "' container logs: " + fmt.Sprint(err)) + s.log("The container might not be running - check logs in " + container.getLogDirPath()) + continue } - fmt.Printf("\nvvvvvvvvvvvvvvv " + + s.log("\nvvvvvvvvvvvvvvv " + container.name + ":\n" + out + "^^^^^^^^^^^^^^^\n\n") - logVppInstance(container, 20) + s.logVppInstance(container, 20) } } @@ -187,9 +200,25 @@ func (s *HstSuite) assertNotEmpty(object interface{}, msgAndArgs ...interface{}) Expect(object).ToNot(BeEmpty(), msgAndArgs...) } +func (s *HstSuite) createLogger() { + suiteName := s.getCurrentSuiteName() + var err error + s.logFile, err = os.Create("summary/" + suiteName + ".log") + if err != nil { + Fail("Unable to create log file.") + } + s.logger = log.New(io.Writer(s.logFile), "", log.LstdFlags) +} + +// Logs to files by default, logs to stdout when VERBOSE=true with GinkgoWriter +// to keep console tidy func (s *HstSuite) log(arg any) { + logs := strings.Split(fmt.Sprint(arg), "\n") + for _, line := range logs { + s.logger.Println(line) + } if *isVerbose { - slog.Info(fmt.Sprint(arg)) + GinkgoWriter.Println(arg) } } @@ -218,8 +247,9 @@ func (s *HstSuite) SkipUnlessExtendedTestsBuilt() { } func (s *HstSuite) resetContainers() { - for _, container := range s.containers { + for _, container := range s.startedContainers { container.stop() + exechelper.Run("docker rm " + container.name) } } @@ -232,15 +262,15 @@ func (s *HstSuite) removeVolumes() { } func (s *HstSuite) getNetNamespaceByName(name string) string { - return name + s.pid + return s.processIndex + name + s.ppid } func (s *HstSuite) getInterfaceByName(name string) *NetInterface { - return s.netInterfaces[name+s.pid] + return s.netInterfaces[s.processIndex+name+s.ppid] } func (s *HstSuite) getContainerByName(name string) *Container { - return s.containers[name+s.pid] + return s.containers[s.processIndex+name+s.ppid] } /* @@ -248,7 +278,7 @@ func (s *HstSuite) getContainerByName(name string) *Container { * are not able to modify the original container and affect other tests by doing that */ func (s *HstSuite) getTransientContainerByName(name string) *Container { - containerCopy := *s.containers[name+s.pid] + containerCopy := *s.containers[s.processIndex+name+s.ppid] return &containerCopy } @@ -266,7 +296,7 @@ func (s *HstSuite) loadContainerTopology(topologyName string) { for _, elem := range yamlTopo.Volumes { volumeMap := elem["volume"].(VolumeConfig) hostDir := volumeMap["host-dir"].(string) - workingVolumeDir := logDir + CurrentSpecReport().LeafNodeText + s.pid + volumeDir + workingVolumeDir := logDir + s.getCurrentTestName() + volumeDir volDirReplacer := strings.NewReplacer("$HST_VOLUME_DIR", workingVolumeDir) hostDir = volDirReplacer.Replace(hostDir) s.volumes = append(s.volumes, hostDir) @@ -276,7 +306,7 @@ func (s *HstSuite) loadContainerTopology(topologyName string) { for _, elem := range yamlTopo.Containers { newContainer, err := newContainer(s, elem) newContainer.suite = s - newContainer.name += newContainer.suite.pid + newContainer.name = newContainer.suite.processIndex + newContainer.name + newContainer.suite.ppid if err != nil { Fail("container config error: " + fmt.Sprint(err)) } @@ -300,26 +330,26 @@ func (s *HstSuite) loadNetworkTopology(topologyName string) { for _, elem := range yamlTopo.Devices { if _, ok := elem["name"]; ok { - elem["name"] = elem["name"].(string) + s.pid + elem["name"] = s.processIndex + elem["name"].(string) + s.ppid } if peer, ok := elem["peer"].(NetDevConfig); ok { if peer["name"].(string) != "" { - peer["name"] = peer["name"].(string) + s.pid + peer["name"] = s.processIndex + peer["name"].(string) + s.ppid } if _, ok := peer["netns"]; ok { - peer["netns"] = peer["netns"].(string) + s.pid + peer["netns"] = s.processIndex + peer["netns"].(string) + s.ppid } } if _, ok := elem["netns"]; ok { - elem["netns"] = elem["netns"].(string) + s.pid + elem["netns"] = s.processIndex + elem["netns"].(string) + s.ppid } if _, ok := elem["interfaces"]; ok { interfaceCount := len(elem["interfaces"].([]interface{})) for i := 0; i < interfaceCount; i++ { - elem["interfaces"].([]interface{})[i] = elem["interfaces"].([]interface{})[i].(string) + s.pid + elem["interfaces"].([]interface{})[i] = s.processIndex + elem["interfaces"].([]interface{})[i].(string) + s.ppid } } @@ -361,6 +391,7 @@ func (s *HstSuite) configureNetworkTopology(topologyName string) { } for _, nc := range s.netConfigs { + s.log(nc.Name()) if err := nc.configure(); err != nil { Fail("Network config error: " + fmt.Sprint(err)) } @@ -377,7 +408,7 @@ func (s *HstSuite) unconfigureNetworkTopology() { } func (s *HstSuite) getTestId() string { - testName := CurrentSpecReport().LeafNodeText + testName := s.getCurrentTestName() if s.testIds == nil { s.testIds = map[string]string{} @@ -390,17 +421,25 @@ func (s *HstSuite) getTestId() string { return s.testIds[testName] } -// Returns last 4 digits of PID -func (s *HstSuite) getPortFromPid() string { - port := s.pid - for len(port) < 4 { +func (s *HstSuite) getCurrentTestName() string { + return strings.Split(CurrentSpecReport().LeafNodeText, "/")[1] +} + +func (s *HstSuite) getCurrentSuiteName() string { + return CurrentSpecReport().ContainerHierarchyTexts[0] +} + +// Returns last 3 digits of PID + Ginkgo process index as the 4th digit +func (s *HstSuite) getPortFromPpid() string { + port := s.ppid + for len(port) < 3 { port += "0" } - return port[len(port)-4:] + return port[len(port)-3:] + s.processIndex } func (s *HstSuite) startServerApp(running chan error, done chan struct{}, env []string) { - cmd := exec.Command("iperf3", "-4", "-s", "-p", s.getPortFromPid()) + cmd := exec.Command("iperf3", "-4", "-s", "-p", s.getPortFromPpid()) if env != nil { cmd.Env = env } @@ -424,7 +463,7 @@ func (s *HstSuite) startClientApp(ipAddress string, env []string, clnCh chan err nTries := 0 for { - cmd := exec.Command("iperf3", "-c", ipAddress, "-u", "-l", "1460", "-b", "10g", "-p", s.getPortFromPid()) + cmd := exec.Command("iperf3", "-c", ipAddress, "-u", "-l", "1460", "-b", "10g", "-p", s.getPortFromPpid()) if env != nil { cmd.Env = env } @@ -446,11 +485,11 @@ func (s *HstSuite) startClientApp(ipAddress string, env []string, clnCh chan err } func (s *HstSuite) startHttpServer(running chan struct{}, done chan struct{}, addressPort, netNs string) { - cmd := newCommand([]string{"./http_server", addressPort, s.pid}, netNs) + cmd := newCommand([]string{"./http_server", addressPort, s.ppid, s.processIndex}, netNs) err := cmd.Start() s.log(cmd) if err != nil { - fmt.Println("Failed to start http server: " + fmt.Sprint(err)) + s.log("Failed to start http server: " + fmt.Sprint(err)) return } running <- struct{}{} @@ -476,3 +515,21 @@ func (s *HstSuite) startWget(finished chan error, server_ip, port, query, netNs } finished <- nil } + +/* +runBenchmark creates Gomega's experiment with the passed-in name and samples the passed-in callback repeatedly (samplesNum times), +passing in suite context, experiment and your data. + +You can also instruct runBenchmark to run with multiple concurrent workers. +You can record multiple named measurements (float64 or duration) within passed-in callback. +runBenchmark then produces report to show statistical distribution of measurements. +*/ +func (s *HstSuite) runBenchmark(name string, samplesNum, parallelNum int, callback func(s *HstSuite, e *gmeasure.Experiment, data interface{}), data interface{}) { + experiment := gmeasure.NewExperiment(name) + + experiment.Sample(func(idx int) { + defer GinkgoRecover() + callback(s, experiment, data) + }, gmeasure.SamplingConfig{N: samplesNum, NumParallel: parallelNum}) + AddReportEntry(experiment.Name, experiment) +} diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go index acd026d484f..ca8b618a7b2 100644 --- a/extras/hs-test/http_test.go +++ b/extras/hs-test/http_test.go @@ -1,7 +1,11 @@ package main import ( + "bytes" "fmt" + "github.com/onsi/gomega/gmeasure" + "io" + "net/http" "os" "strings" "time" @@ -10,32 +14,45 @@ import ( ) func init() { - registerNsTests(HttpTpsTest) - registerVethTests(HttpCliTest) + registerVethTests(HttpCliTest, HttpCliConnectErrorTest) registerNoTopoTests(NginxHttp3Test, NginxAsServerTest, - NginxPerfCpsTest, NginxPerfRpsTest, NginxPerfWrkTest) - registerNoTopoSoloTests(HttpStaticPromTest) + NginxPerfCpsTest, NginxPerfRpsTest, NginxPerfWrkTest, HeaderServerTest, + HttpStaticMovedTest, HttpStaticNotFoundTest, HttpCliMethodNotAllowedTest, + HttpCliBadRequestTest, HttpStaticBuildInUrlGetIfStatsTest, HttpStaticBuildInUrlPostIfStatsTest, + HttpInvalidRequestLineTest, HttpMethodNotImplementedTest, HttpInvalidHeadersTest, + HttpContentLengthTest, HttpStaticBuildInUrlGetIfListTest, HttpStaticBuildInUrlGetVersionTest, + HttpStaticMacTimeTest, HttpStaticBuildInUrlGetVersionVerboseTest, HttpVersionNotSupportedTest, + HttpInvalidContentLengthTest, HttpInvalidTargetSyntaxTest, HttpStaticPathTraversalTest, HttpUriDecodeTest, + HttpHeadersTest) + registerNoTopoSoloTests(HttpStaticPromTest, HttpTpsTest) } -func HttpTpsTest(s *NsSuite) { - iface := s.getInterfaceByName(clientInterface) - client_ip := iface.ip4AddressString() - port := "8080" - finished := make(chan error, 1) - clientNetns := s.getNetNamespaceByName("cln") +const wwwRootPath = "/tmp/www_root" - container := s.getContainerByName("vpp") +func httpDownloadBenchmark(s *HstSuite, experiment *gmeasure.Experiment, data interface{}) { + url, isValid := data.(string) + s.assertEqual(true, isValid) + client := newHttpClient() + req, err := http.NewRequest("GET", url, nil) + s.assertNil(err, fmt.Sprint(err)) + t := time.Now() + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(200, resp.StatusCode) + _, err = io.ReadAll(resp.Body) + duration := time.Since(t) + experiment.RecordValue("Download Speed", (float64(resp.ContentLength)/1024/1024)/duration.Seconds(), gmeasure.Units("MB/s"), gmeasure.Precision(2)) +} - // configure vpp in the container - container.vppInstance.vppctl("http tps uri tcp://0.0.0.0/8080") +func HttpTpsTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + url := "http://" + serverAddress + ":8080/test_file_10M" - go func() { - defer GinkgoRecover() - s.startWget(finished, client_ip, port, "test_file_10M", clientNetns) - }() - // wait for client - err := <-finished - s.assertNil(err, fmt.Sprint(err)) + vpp.vppctl("http tps uri tcp://0.0.0.0/8080") + + s.runBenchmark("HTTP tps 10M", 10, 0, httpDownloadBenchmark, url) } func HttpCliTest(s *VethsSuite) { @@ -55,6 +72,20 @@ func HttpCliTest(s *VethsSuite) { s.assertContains(o, "<html>", "<html> not found in the result!") } +func HttpCliConnectErrorTest(s *VethsSuite) { + clientContainer := s.getContainerByName("client-vpp") + + serverVeth := s.getInterfaceByName(serverInterfaceName) + + uri := "http://" + serverVeth.ip4AddressString() + "/80" + + o := clientContainer.vppInstance.vppctl("http cli client" + + " uri " + uri + " query /show/vlib/graph") + + s.log(o) + s.assertContains(o, "failed to connect") +} + func NginxHttp3Test(s *NoTopoSuite) { s.SkipUnlessExtendedTestsBuilt() @@ -71,6 +102,7 @@ func NginxHttp3Test(s *NoTopoSuite) { args := fmt.Sprintf("curl --noproxy '*' --local-port 55444 --http3-only -k https://%s:8443/%s", serverAddress, query) curlCont.extraRunningArgs = args o, err := curlCont.combinedOutput() + s.log(o) s.assertNil(err, fmt.Sprint(err)) s.assertContains(o, "<http>", "<http> not found in the result!") } @@ -91,6 +123,444 @@ func HttpStaticPromTest(s *NoTopoSuite) { s.assertNil(err) } +func HttpStaticPathTraversalTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + vpp.container.exec("mkdir -p " + wwwRootPath) + vpp.container.exec("mkdir -p " + "/tmp/secret_folder") + vpp.container.createFile("/tmp/secret_folder/secret_file.txt", "secret") + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug")) + + client := newHttpClient() + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/../secret_folder/secret_file.txt", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(404, resp.StatusCode) +} + +func HttpStaticMovedTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + vpp.container.exec("mkdir -p " + wwwRootPath + "/tmp.aaa") + vpp.container.createFile(wwwRootPath+"/tmp.aaa/index.html", "<http><body><p>Hello</p></body></http>") + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug")) + + client := newHttpClient() + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/tmp.aaa", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(301, resp.StatusCode) + s.assertNotEqual("", resp.Header.Get("Location")) +} + +func HttpStaticNotFoundTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + vpp.container.exec("mkdir -p " + wwwRootPath) + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug")) + + client := newHttpClient() + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/notfound.html", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(404, resp.StatusCode) +} + +func HttpCliMethodNotAllowedTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + client := newHttpClient() + req, err := http.NewRequest("POST", "http://"+serverAddress+":80/test", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(405, resp.StatusCode) + // TODO: need to be fixed in http code + //s.assertNotEqual("", resp.Header.Get("Allow")) +} + +func HttpCliBadRequestTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + client := newHttpClient() + req, err := http.NewRequest("GET", "http://"+serverAddress+":80", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(400, resp.StatusCode) +} + +func HttpStaticBuildInUrlGetVersionTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug")) + + client := newHttpClient() + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/version.json", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(200, resp.StatusCode) + data, err := io.ReadAll(resp.Body) + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(string(data), "vpp_details") + s.assertContains(string(data), "version") + s.assertContains(string(data), "build_date") + s.assertNotContains(string(data), "build_by") + s.assertNotContains(string(data), "build_host") + s.assertNotContains(string(data), "build_dir") +} + +func HttpStaticBuildInUrlGetVersionVerboseTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug")) + + client := newHttpClient() + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/version.json?verbose=true", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(200, resp.StatusCode) + data, err := io.ReadAll(resp.Body) + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(string(data), "vpp_details") + s.assertContains(string(data), "version") + s.assertContains(string(data), "build_date") + s.assertContains(string(data), "build_by") + s.assertContains(string(data), "build_host") + s.assertContains(string(data), "build_dir") +} + +func HttpStaticBuildInUrlGetIfListTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug")) + + client := newHttpClient() + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/interface_list.json", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(200, resp.StatusCode) + data, err := io.ReadAll(resp.Body) + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(string(data), "interface_list") + s.assertContains(string(data), s.getInterfaceByName(tapInterfaceName).peer.Name()) +} + +func HttpStaticBuildInUrlGetIfStatsTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug")) + + client := newHttpClient() + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/interface_stats.json", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(200, resp.StatusCode) + data, err := io.ReadAll(resp.Body) + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(string(data), "interface_stats") + s.assertContains(string(data), "local0") + s.assertContains(string(data), s.getInterfaceByName(tapInterfaceName).peer.Name()) +} + +func validatePostInterfaceStats(s *NoTopoSuite, data string) { + s.assertContains(data, "interface_stats") + s.assertContains(data, s.getInterfaceByName(tapInterfaceName).peer.Name()) + s.assertNotContains(data, "error") + s.assertNotContains(data, "local0") +} + +func HttpStaticBuildInUrlPostIfStatsTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug")) + body := []byte(s.getInterfaceByName(tapInterfaceName).peer.Name()) + + client := newHttpClient() + req, err := http.NewRequest("POST", + "http://"+serverAddress+":80/interface_stats.json", bytes.NewBuffer(body)) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(200, resp.StatusCode) + data, err := io.ReadAll(resp.Body) + s.assertNil(err, fmt.Sprint(err)) + validatePostInterfaceStats(s, string(data)) +} + +func HttpStaticMacTimeTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug")) + s.log(vpp.vppctl("mactime enable-disable " + s.getInterfaceByName(tapInterfaceName).peer.Name())) + + client := newHttpClient() + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/mactime.json", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(200, resp.StatusCode) + data, err := io.ReadAll(resp.Body) + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(string(data), "mactime") + s.assertContains(string(data), s.getInterfaceByName(tapInterfaceName).ip4AddressString()) + s.assertContains(string(data), s.getInterfaceByName(tapInterfaceName).hwAddress.String()) +} + +func HttpInvalidRequestLineTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + resp, err := tcpSendReceive(serverAddress+":80", "GET / HTTP/1.1") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "invalid framing not allowed") + + resp, err = tcpSendReceive(serverAddress+":80", "GET / HTTP/1.1\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "invalid framing not allowed") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "HTTP-version must be present") + + resp, err = tcpSendReceive(serverAddress+":80", "GET HTTP/1.1\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "request-target must be present") + + resp, err = tcpSendReceive(serverAddress+":80", "GET HTTP/1.1\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "request-target must be present") + + resp, err = tcpSendReceive(serverAddress+":80", "GET / HTTP/x\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "'HTTP/x' invalid http version not allowed") + + resp, err = tcpSendReceive(serverAddress+":80", "GET / HTTP1.1\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "'HTTP1.1' invalid http version not allowed") +} + +func HttpInvalidTargetSyntaxTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug")) + + resp, err := tcpSendReceive(serverAddress+":80", "GET /interface|stats.json HTTP/1.1\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "'|' not allowed in target path") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /interface#stats.json HTTP/1.1\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "'#' not allowed in target path") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /interface%stats.json HTTP/1.1\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", + "after '%' there must be two hex-digit characters in target path") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /interface%1stats.json HTTP/1.1\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", + "after '%' there must be two hex-digit characters in target path") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /interface%Bstats.json HTTP/1.1\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", + "after '%' there must be two hex-digit characters in target path") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /interface%stats.json%B HTTP/1.1\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", + "after '%' there must be two hex-digit characters in target path") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /version.json?verbose>true HTTP/1.1\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "'>' not allowed in target query") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /version.json?verbose%true HTTP/1.1\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", + "after '%' there must be two hex-digit characters in target query") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /version.json?verbose=%1 HTTP/1.1\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", + "after '%' there must be two hex-digit characters in target query") +} + +func HttpInvalidContentLengthTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + resp, err := tcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nContent-Length:\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "Content-Length value must be present") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nContent-Length: \r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "Content-Length value must be present") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nContent-Length: a\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", + "Content-Length value other than digit not allowed") +} + +func HttpContentLengthTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug")) + ifName := s.getInterfaceByName(tapInterfaceName).peer.Name() + + resp, err := tcpSendReceive(serverAddress+":80", + "POST /interface_stats.json HTTP/1.1\r\nContent-Length:4\r\n\r\n"+ifName) + s.assertNil(err, fmt.Sprint(err)) + validatePostInterfaceStats(s, resp) + + resp, err = tcpSendReceive(serverAddress+":80", + "POST /interface_stats.json HTTP/1.1\r\n Content-Length: 4 \r\n\r\n"+ifName) + s.assertNil(err, fmt.Sprint(err)) + validatePostInterfaceStats(s, resp) + + resp, err = tcpSendReceive(serverAddress+":80", + "POST /interface_stats.json HTTP/1.1\r\n\tContent-Length:\t\t4\r\n\r\n"+ifName) + s.assertNil(err, fmt.Sprint(err)) + validatePostInterfaceStats(s, resp) +} + +func HttpMethodNotImplementedTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + client := newHttpClient() + req, err := http.NewRequest("OPTIONS", "http://"+serverAddress+":80/show/version", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(501, resp.StatusCode) +} + +func HttpVersionNotSupportedTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + resp, err := tcpSendReceive(serverAddress+":80", "GET / HTTP/2\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 505 HTTP Version Not Supported") +} + +func HttpUriDecodeTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + client := newHttpClient() + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/sh%6fw%20versio%6E%20verbose", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(200, resp.StatusCode) + data, err := io.ReadAll(resp.Body) + s.assertNil(err, fmt.Sprint(err)) + s.log(string(data)) + s.assertNotContains(string(data), "unknown input") + s.assertContains(string(data), "Compiler") +} + +func HttpHeadersTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + resp, err := tcpSendReceive( + serverAddress+":80", + "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\nUser-Agent:test\r\nAccept:text/xml\r\nAccept:\ttext/plain\t \r\nAccept:text/html\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 200 OK") + s.assertContains(resp, "Content-Type: text / plain") + s.assertNotContains(resp, "<html>", "html content received instead of plain text") +} + +func HttpInvalidHeadersTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + resp, err := tcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nUser-Agent: test\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "Header section must end with CRLF CRLF") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\nUser@Agent:test\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "'@' not allowed in field name") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\nUser-Agent\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "incomplete field line not allowed") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\n: test\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "empty field name not allowed") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\rUser-Agent:test\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "invalid field line end not allowed") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\nUser-Agent:test\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "invalid field line end not allowed") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\nUser-Agent:\r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "empty field value not allowed") + + resp, err = tcpSendReceive(serverAddress+":80", "GET /show/version HTTP/1.1\r\nHost:"+serverAddress+":80\r\nUser-Agent: \r\n\r\n") + s.assertNil(err, fmt.Sprint(err)) + s.assertContains(resp, "HTTP/1.1 400 Bad Request", "empty field value not allowed") +} + +func HeaderServerTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + client := newHttpClient() + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/show/version", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual("http_cli_server", resp.Header.Get("Server")) +} + func NginxAsServerTest(s *NoTopoSuite) { query := "return_ok" finished := make(chan error, 1) @@ -145,7 +615,6 @@ func runNginxPerf(s *NoTopoSuite, mode, ab_or_wrk string) error { args += " -r" args += " http://" + serverAddress + ":80/64B.json" abCont.extraRunningArgs = args - time.Sleep(time.Second * 10) o, err := abCont.combinedOutput() rps := parseString(o, "Requests per second:") s.log(rps) @@ -165,7 +634,9 @@ func runNginxPerf(s *NoTopoSuite, mode, ab_or_wrk string) error { return nil } +// unstable with multiple workers func NginxPerfCpsTest(s *NoTopoSuite) { + s.SkipIfMultiWorker() s.assertNil(runNginxPerf(s, "cps", "ab")) } diff --git a/extras/hs-test/mirroring_test.go b/extras/hs-test/mirroring_test.go index 6c5a860b01c..1fd15dde2f4 100644 --- a/extras/hs-test/mirroring_test.go +++ b/extras/hs-test/mirroring_test.go @@ -8,7 +8,9 @@ func init() { registerNginxTests(MirroringTest) } +// broken when CPUS > 1 func MirroringTest(s *NginxSuite) { + s.SkipIfMultiWorker() proxyAddress := s.getInterfaceByName(mirroringClientInterfaceName).peer.ip4AddressString() path := "/64B.json" diff --git a/extras/hs-test/proxy_test.go b/extras/hs-test/proxy_test.go index ac5f94c8535..c57b927846e 100644 --- a/extras/hs-test/proxy_test.go +++ b/extras/hs-test/proxy_test.go @@ -13,9 +13,9 @@ func init() { } func testProxyHttpTcp(s *NsSuite, proto string) error { - var outputFile string = "test" + s.pid + ".data" - var srcFilePid string = "httpTestFile" + s.pid - const srcFileNoPid = "httpTestFile" + var outputFile string = s.processIndex + "test" + s.ppid + ".data" + var srcFilePpid string = s.processIndex + "httpTestFile" + s.ppid + const srcFileNoPpid = "httpTestFile" const fileSize string = "10M" stopServer := make(chan struct{}, 1) serverRunning := make(chan struct{}, 1) @@ -23,9 +23,9 @@ func testProxyHttpTcp(s *NsSuite, proto string) error { clientNetns := s.getNetNamespaceByName("cln") // create test file - err := exechelper.Run(fmt.Sprintf("ip netns exec %s truncate -s %s %s", serverNetns, fileSize, srcFilePid)) + err := exechelper.Run(fmt.Sprintf("ip netns exec %s truncate -s %s %s", serverNetns, fileSize, srcFilePpid)) s.assertNil(err, "failed to run truncate command: "+fmt.Sprint(err)) - defer func() { os.Remove(srcFilePid) }() + defer func() { os.Remove(srcFilePpid) }() s.log("test file created...") @@ -48,7 +48,7 @@ func testProxyHttpTcp(s *NsSuite, proto string) error { if proto == "tls" { c += " --secure-protocol=TLSv1_3 --no-check-certificate https://" } - c += fmt.Sprintf("%s:555/%s", clientVeth.ip4AddressString(), srcFileNoPid) + c += fmt.Sprintf("%s:555/%s", clientVeth.ip4AddressString(), srcFileNoPpid) s.log(c) _, err = exechelper.CombinedOutput(c) @@ -57,7 +57,7 @@ func testProxyHttpTcp(s *NsSuite, proto string) error { s.assertNil(err, "failed to run wget: '%s', cmd: %s", err, c) stopServer <- struct{}{} - s.assertNil(assertFileSize(outputFile, srcFilePid)) + s.assertNil(assertFileSize(outputFile, srcFilePpid)) return nil } diff --git a/extras/hs-test/raw_session_test.go b/extras/hs-test/raw_session_test.go index cf74c62cd3e..5c66df0b1ce 100644 --- a/extras/hs-test/raw_session_test.go +++ b/extras/hs-test/raw_session_test.go @@ -1,16 +1,15 @@ package main func init() { - registerVethTests(VppEchoQuicTest, VppEchoTcpTest, VppEchoUdpTest) + registerVethTests(VppEchoQuicTest, VppEchoTcpTest) } func VppEchoQuicTest(s *VethsSuite) { s.testVppEcho("quic") } -// udp echo currently broken in vpp, skipping +// TODO: udp echo currently broken in vpp func VppEchoUdpTest(s *VethsSuite) { - s.skip("Broken") s.testVppEcho("udp") } diff --git a/extras/hs-test/script/build_hst.sh b/extras/hs-test/script/build_hst.sh index cc2d00b6cbd..3b4bc28e275 100755 --- a/extras/hs-test/script/build_hst.sh +++ b/extras/hs-test/script/build_hst.sh @@ -21,6 +21,8 @@ export VPP_WS=../.. if [ "$1" == "debug" ]; then VPP_BUILD_ROOT=${VPP_WS}/build-root/build-vpp_debug-native/vpp +elif [ "$1" == "gcov" ]; then + VPP_BUILD_ROOT=${VPP_WS}/build-root/build-vpp_gcov-native/vpp else VPP_BUILD_ROOT=${VPP_WS}/build-root/build-vpp-native/vpp fi diff --git a/extras/hs-test/script/compress.sh b/extras/hs-test/script/compress.sh new file mode 100644 index 00000000000..1f0205c1efb --- /dev/null +++ b/extras/hs-test/script/compress.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# if failed-summary.log is not empty, exit status = 1 +if [ -s "${HS_ROOT}/summary/failed-summary.log" ] +then + if [ -n "${WORKSPACE}" ] + then + echo -n "Copying docker logs..." + dirs=$(jq -r '.[0] | .SpecReports[] | select(.State == "failed") | .LeafNodeText' ${HS_ROOT}/summary/report.json) + for dirName in $dirs; do + logDir=/tmp/hs-test/$dirName + if [ -d "$logDir" ]; then + mkdir -p ${WORKSPACE}/archives/summary + cp -r $logDir ${WORKSPACE}/archives/summary/ + fi + done + echo "Done." + + echo -n "Copying failed test logs into build log archive directory (${WORKSPACE}/archives)... " + mkdir -p ${WORKSPACE}/archives/summary + cp -a ${HS_ROOT}/summary/* ${WORKSPACE}/archives/summary + echo "Done." + + echo -n "Compressing files in ${WORKSPACE}/archives from test runs... " + cd ${WORKSPACE}/archives + find . -type f \( -name "*.json" -o -name "*.log" \) -exec gzip {} \; + echo "Done." + + else + echo "Not compressing files in temporary directories from test runs." + fi + exit 1 +fi diff --git a/extras/hs-test/script/nginx_ldp.sh b/extras/hs-test/script/nginx_ldp.sh index 4a22e14aaf7..a6b0a20e47b 100755 --- a/extras/hs-test/script/nginx_ldp.sh +++ b/extras/hs-test/script/nginx_ldp.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -LD_PRELOAD=$LDP $@ 2>&1 > /proc/1/fd/1 +$1 -v && LD_PRELOAD=$LDP $@ 2>&1 > /proc/1/fd/1 diff --git a/extras/hs-test/suite_nginx_test.go b/extras/hs-test/suite_nginx_test.go index 2d1caf152f4..7caf16ef432 100644 --- a/extras/hs-test/suite_nginx_test.go +++ b/extras/hs-test/suite_nginx_test.go @@ -4,7 +4,6 @@ import ( "reflect" "runtime" "strings" - "time" . "github.com/onsi/ginkgo/v2" ) @@ -18,18 +17,18 @@ const ( mirroringServerInterfaceName = "hstsrv" ) -var nginxTests = []func(s *NginxSuite){} -var nginxSoloTests = []func(s *NginxSuite){} +var nginxTests = map[string][]func(s *NginxSuite){} +var nginxSoloTests = map[string][]func(s *NginxSuite){} type NginxSuite struct { HstSuite } func registerNginxTests(tests ...func(s *NginxSuite)) { - nginxTests = append(nginxTests, tests...) + nginxTests[getTestFilename()] = tests } func registerNginxSoloTests(tests ...func(s *NginxSuite)) { - nginxSoloTests = append(nginxSoloTests, tests...) + nginxSoloTests[getTestFilename()] = tests } func (s *NginxSuite) SetupSuite() { @@ -48,10 +47,9 @@ func (s *NginxSuite) SetupTest() { append("enable"). append("use-app-socket-api").close() - cpus := s.AllocateCpus() // ... for proxy vppProxyContainer := s.getContainerByName(vppProxyContainerName) - proxyVpp, _ := vppProxyContainer.newVppInstance(cpus, sessionConfig) + proxyVpp, _ := vppProxyContainer.newVppInstance(vppProxyContainer.allocatedCpus, sessionConfig) s.assertNil(proxyVpp.start()) clientInterface := s.getInterfaceByName(mirroringClientInterfaceName) @@ -94,13 +92,18 @@ var _ = Describe("NginxSuite", Ordered, ContinueOnFailure, func() { AfterEach(func() { s.TearDownTest() }) - for _, test := range nginxTests { - test := test - pc := reflect.ValueOf(test).Pointer() - funcValue := runtime.FuncForPC(pc) - It(strings.Split(funcValue.Name(), ".")[2], func(ctx SpecContext) { - test(&s) - }, SpecTimeout(time.Minute*5)) + + for filename, tests := range nginxTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(suiteTimeout)) + } } }) @@ -118,12 +121,17 @@ var _ = Describe("NginxSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { AfterEach(func() { s.TearDownTest() }) - for _, test := range nginxSoloTests { - test := test - pc := reflect.ValueOf(test).Pointer() - funcValue := runtime.FuncForPC(pc) - It(strings.Split(funcValue.Name(), ".")[2], Label("SOLO"), func(ctx SpecContext) { - test(&s) - }, SpecTimeout(time.Minute*5)) + + for filename, tests := range nginxSoloTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, Label("SOLO"), func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(suiteTimeout)) + } } }) diff --git a/extras/hs-test/suite_no_topo_test.go b/extras/hs-test/suite_no_topo_test.go index 6df06c7e60e..af0cf9a197a 100644 --- a/extras/hs-test/suite_no_topo_test.go +++ b/extras/hs-test/suite_no_topo_test.go @@ -4,7 +4,6 @@ import ( "reflect" "runtime" "strings" - "time" . "github.com/onsi/ginkgo/v2" ) @@ -15,18 +14,18 @@ const ( tapInterfaceName = "htaphost" ) -var noTopoTests = []func(s *NoTopoSuite){} -var noTopoSoloTests = []func(s *NoTopoSuite){} +var noTopoTests = map[string][]func(s *NoTopoSuite){} +var noTopoSoloTests = map[string][]func(s *NoTopoSuite){} type NoTopoSuite struct { HstSuite } func registerNoTopoTests(tests ...func(s *NoTopoSuite)) { - noTopoTests = append(noTopoTests, tests...) + noTopoTests[getTestFilename()] = tests } func registerNoTopoSoloTests(tests ...func(s *NoTopoSuite)) { - noTopoSoloTests = append(noTopoSoloTests, tests...) + noTopoSoloTests[getTestFilename()] = tests } func (s *NoTopoSuite) SetupSuite() { @@ -45,9 +44,8 @@ func (s *NoTopoSuite) SetupTest() { append("enable"). append("use-app-socket-api").close() - cpus := s.AllocateCpus() container := s.getContainerByName(singleTopoContainerVpp) - vpp, _ := container.newVppInstance(cpus, sessionConfig) + vpp, _ := container.newVppInstance(container.allocatedCpus, sessionConfig) s.assertNil(vpp.start()) tapInterface := s.getInterfaceByName(tapInterfaceName) @@ -70,13 +68,17 @@ var _ = Describe("NoTopoSuite", Ordered, ContinueOnFailure, func() { s.TearDownTest() }) - for _, test := range noTopoTests { - test := test - pc := reflect.ValueOf(test).Pointer() - funcValue := runtime.FuncForPC(pc) - It(strings.Split(funcValue.Name(), ".")[2], func(ctx SpecContext) { - test(&s) - }, SpecTimeout(time.Minute*5)) + for filename, tests := range noTopoTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(suiteTimeout)) + } } }) @@ -95,12 +97,16 @@ var _ = Describe("NoTopoSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { s.TearDownTest() }) - for _, test := range noTopoSoloTests { - test := test - pc := reflect.ValueOf(test).Pointer() - funcValue := runtime.FuncForPC(pc) - It(strings.Split(funcValue.Name(), ".")[2], Label("SOLO"), func(ctx SpecContext) { - test(&s) - }, SpecTimeout(time.Minute*5)) + for filename, tests := range noTopoSoloTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, Label("SOLO"), func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(suiteTimeout)) + } } }) diff --git a/extras/hs-test/suite_ns_test.go b/extras/hs-test/suite_ns_test.go index 86d9b78a010..b4fa7718041 100644 --- a/extras/hs-test/suite_ns_test.go +++ b/extras/hs-test/suite_ns_test.go @@ -5,7 +5,6 @@ import ( "reflect" "runtime" "strings" - "time" . "github.com/onsi/ginkgo/v2" ) @@ -16,18 +15,18 @@ const ( serverInterface = "hsrvvpp" ) -var nsTests = []func(s *NsSuite){} -var nsSoloTests = []func(s *NsSuite){} +var nsTests = map[string][]func(s *NsSuite){} +var nsSoloTests = map[string][]func(s *NsSuite){} type NsSuite struct { HstSuite } func registerNsTests(tests ...func(s *NsSuite)) { - nsTests = append(nsTests, tests...) + nsTests[getTestFilename()] = tests } func registerNsSoloTests(tests ...func(s *NsSuite)) { - nsSoloTests = append(nsSoloTests, tests...) + nsSoloTests[getTestFilename()] = tests } func (s *NsSuite) SetupSuite() { @@ -48,9 +47,8 @@ func (s *NsSuite) SetupTest() { append("evt_qs_memfd_seg"). append("event-queue-length 100000").close() - cpus := s.AllocateCpus() container := s.getContainerByName("vpp") - vpp, _ := container.newVppInstance(cpus, sessionConfig) + vpp, _ := container.newVppInstance(container.allocatedCpus, sessionConfig) s.assertNil(vpp.start()) idx, err := vpp.createAfPacket(s.getInterfaceByName(serverInterface)) @@ -79,13 +77,17 @@ var _ = Describe("NsSuite", Ordered, ContinueOnFailure, func() { s.TearDownTest() }) - for _, test := range nsTests { - test := test - pc := reflect.ValueOf(test).Pointer() - funcValue := runtime.FuncForPC(pc) - It(strings.Split(funcValue.Name(), ".")[2], func(ctx SpecContext) { - test(&s) - }, SpecTimeout(time.Minute*5)) + for filename, tests := range nsTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(suiteTimeout)) + } } }) @@ -104,12 +106,16 @@ var _ = Describe("NsSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { s.TearDownTest() }) - for _, test := range nsSoloTests { - test := test - pc := reflect.ValueOf(test).Pointer() - funcValue := runtime.FuncForPC(pc) - It(strings.Split(funcValue.Name(), ".")[2], Label("SOLO"), func(ctx SpecContext) { - test(&s) - }, SpecTimeout(time.Minute*5)) + for filename, tests := range nsSoloTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, Label("SOLO"), func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(suiteTimeout)) + } } }) diff --git a/extras/hs-test/suite_tap_test.go b/extras/hs-test/suite_tap_test.go index 25c1e25a215..bb7082de480 100644 --- a/extras/hs-test/suite_tap_test.go +++ b/extras/hs-test/suite_tap_test.go @@ -13,14 +13,14 @@ type TapSuite struct { HstSuite } -var tapTests = []func(s *TapSuite){} -var tapSoloTests = []func(s *TapSuite){} +var tapTests = map[string][]func(s *TapSuite){} +var tapSoloTests = map[string][]func(s *TapSuite){} func registerTapTests(tests ...func(s *TapSuite)) { - tapTests = append(tapTests, tests...) + tapTests[getTestFilename()] = tests } func registerTapSoloTests(tests ...func(s *TapSuite)) { - tapSoloTests = append(tapSoloTests, tests...) + tapSoloTests[getTestFilename()] = tests } func (s *TapSuite) SetupSuite() { @@ -44,13 +44,17 @@ var _ = Describe("TapSuite", Ordered, ContinueOnFailure, func() { s.TearDownTest() }) - for _, test := range tapTests { - test := test - pc := reflect.ValueOf(test).Pointer() - funcValue := runtime.FuncForPC(pc) - It(strings.Split(funcValue.Name(), ".")[2], func(ctx SpecContext) { - test(&s) - }, SpecTimeout(time.Minute*5)) + for filename, tests := range tapTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(suiteTimeout)) + } } }) @@ -69,12 +73,16 @@ var _ = Describe("TapSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { s.TearDownTest() }) - for _, test := range tapSoloTests { - test := test - pc := reflect.ValueOf(test).Pointer() - funcValue := runtime.FuncForPC(pc) - It(strings.Split(funcValue.Name(), ".")[2], Label("SOLO"), func(ctx SpecContext) { - test(&s) - }, SpecTimeout(time.Minute*5)) + for filename, tests := range tapSoloTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, Label("SOLO"), func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(suiteTimeout)) + } } }) diff --git a/extras/hs-test/suite_veth_test.go b/extras/hs-test/suite_veth_test.go index f4c3684f031..49a36cacb83 100644 --- a/extras/hs-test/suite_veth_test.go +++ b/extras/hs-test/suite_veth_test.go @@ -16,18 +16,18 @@ const ( clientInterfaceName = "cln" ) -var vethTests = []func(s *VethsSuite){} -var vethSoloTests = []func(s *VethsSuite){} +var vethTests = map[string][]func(s *VethsSuite){} +var vethSoloTests = map[string][]func(s *VethsSuite){} type VethsSuite struct { HstSuite } func registerVethTests(tests ...func(s *VethsSuite)) { - vethTests = append(vethTests, tests...) + vethTests[getTestFilename()] = tests } func registerSoloVethTests(tests ...func(s *VethsSuite)) { - vethSoloTests = append(vethSoloTests, tests...) + vethSoloTests[getTestFilename()] = tests } func (s *VethsSuite) SetupSuite() { @@ -50,8 +50,7 @@ func (s *VethsSuite) SetupTest() { // ... For server serverContainer := s.getContainerByName("server-vpp") - cpus := s.AllocateCpus() - serverVpp, err := serverContainer.newVppInstance(cpus, sessionConfig) + serverVpp, err := serverContainer.newVppInstance(serverContainer.allocatedCpus, sessionConfig) s.assertNotNil(serverVpp, fmt.Sprint(err)) s.setupServerVpp() @@ -59,8 +58,7 @@ func (s *VethsSuite) SetupTest() { // ... For client clientContainer := s.getContainerByName("client-vpp") - cpus = s.AllocateCpus() - clientVpp, err := clientContainer.newVppInstance(cpus, sessionConfig) + clientVpp, err := clientContainer.newVppInstance(clientContainer.allocatedCpus, sessionConfig) s.assertNotNil(clientVpp, fmt.Sprint(err)) s.setupClientVpp() @@ -103,13 +101,17 @@ var _ = Describe("VethsSuite", Ordered, ContinueOnFailure, func() { }) // https://onsi.github.io/ginkgo/#dynamically-generating-specs - for _, test := range vethTests { - test := test - pc := reflect.ValueOf(test).Pointer() - funcValue := runtime.FuncForPC(pc) - It(strings.Split(funcValue.Name(), ".")[2], func(ctx SpecContext) { - test(&s) - }, SpecTimeout(time.Minute*5)) + for filename, tests := range vethTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(suiteTimeout)) + } } }) @@ -123,19 +125,22 @@ var _ = Describe("VethsSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { }) AfterAll(func() { s.TearDownSuite() - }) AfterEach(func() { s.TearDownTest() }) // https://onsi.github.io/ginkgo/#dynamically-generating-specs - for _, test := range vethSoloTests { - test := test - pc := reflect.ValueOf(test).Pointer() - funcValue := runtime.FuncForPC(pc) - It(strings.Split(funcValue.Name(), ".")[2], Label("SOLO"), func(ctx SpecContext) { - test(&s) - }, SpecTimeout(time.Minute*5)) + for filename, tests := range vethSoloTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, Label("SOLO"), func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(suiteTimeout)) + } } }) diff --git a/extras/hs-test/test b/extras/hs-test/test index 94162b87759..398e2b39edb 100755..100644 --- a/extras/hs-test/test +++ b/extras/hs-test/test @@ -86,4 +86,9 @@ if [ $single_test -eq 0 ] && [ $debug_set -eq 1 ]; then exit 1 fi -sudo -E go run github.com/onsi/ginkgo/v2/ginkgo --no-color --trace $ginkgo_args -- $args +mkdir -p summary + +sudo -E go run github.com/onsi/ginkgo/v2/ginkgo --no-color --trace --json-report=summary/report.json $ginkgo_args -- $args + +jq -r '.[0] | .SpecReports[] | select((.State == "failed") or (.State == "timedout") or (.State == "panicked")) | select(.Failure != null) | "TestName: \(.LeafNodeText)\nSuite:\n\(.Failure.Location.FileName)\nMessage:\n\(.Failure.Message)\n Full Stack Trace:\n\(.Failure.Location.FullStackTrace)\n"' summary/report.json > summary/failed-summary.log \ + && echo "Summary generated -> summary/failed-summary.log"
\ No newline at end of file diff --git a/extras/hs-test/tools/http_server/http_server.go b/extras/hs-test/tools/http_server/http_server.go index d2ab3851643..a65aa7556e7 100644 --- a/extras/hs-test/tools/http_server/http_server.go +++ b/extras/hs-test/tools/http_server/http_server.go @@ -8,13 +8,13 @@ import ( ) func main() { - if len(os.Args) < 3 { + if len(os.Args) < 4 { fmt.Println("arg expected") os.Exit(1) } http.HandleFunc("/httpTestFile", func(w http.ResponseWriter, r *http.Request) { - file, _ := os.Open("httpTestFile" + os.Args[2]) + file, _ := os.Open(os.Args[3] + "httpTestFile" + os.Args[2]) defer file.Close() io.Copy(w, file) }) diff --git a/extras/hs-test/utils.go b/extras/hs-test/utils.go index 304dd4c241b..b61ac4271d6 100644 --- a/extras/hs-test/utils.go +++ b/extras/hs-test/utils.go @@ -3,8 +3,11 @@ package main import ( "fmt" "io" + "net" + "net/http" "os" "strings" + "time" ) const networkTopologyDir string = "topo-network/" @@ -78,3 +81,39 @@ func (s *Stanza) saveToFile(fileName string) error { _, err = io.Copy(fo, strings.NewReader(s.content)) return err } + +// newHttpClient creates [http.Client] with disabled proxy and redirects, it also sets timeout to 30seconds. +func newHttpClient() *http.Client { + transport := http.DefaultTransport + transport.(*http.Transport).Proxy = nil + transport.(*http.Transport).DisableKeepAlives = true + client := &http.Client{ + Transport: transport, + Timeout: time.Second * 30, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }} + return client +} + +func tcpSendReceive(address, data string) (string, error) { + conn, err := net.DialTimeout("tcp", address, time.Second*30) + if err != nil { + return "", err + } + defer conn.Close() + err = conn.SetDeadline(time.Now().Add(time.Second * 30)) + if err != nil { + return "", err + } + _, err = conn.Write([]byte(data)) + if err != nil { + return "", err + } + reply := make([]byte, 1024) + _, err = conn.Read(reply) + if err != nil { + return "", err + } + return string(reply), nil +} diff --git a/extras/hs-test/vppinstance.go b/extras/hs-test/vppinstance.go index 7ddec7e0ed4..4b2c7551034 100644 --- a/extras/hs-test/vppinstance.go +++ b/extras/hs-test/vppinstance.go @@ -1,7 +1,11 @@ package main import ( + "context" "fmt" + "go.fd.io/govpp/binapi/ethernet_types" + "io" + "net" "os" "os/exec" "os/signal" @@ -12,6 +16,7 @@ import ( "github.com/edwarnicke/exechelper" . "github.com/onsi/ginkgo/v2" + "github.com/sirupsen/logrus" "go.fd.io/govpp" "go.fd.io/govpp/api" @@ -20,7 +25,6 @@ import ( "go.fd.io/govpp/binapi/interface_types" "go.fd.io/govpp/binapi/session" "go.fd.io/govpp/binapi/tapv2" - "go.fd.io/govpp/binapi/vpe" "go.fd.io/govpp/core" ) @@ -62,6 +66,7 @@ plugins { plugin tlsopenssl_plugin.so { enable } plugin ping_plugin.so { enable } plugin nsim_plugin.so { enable } + plugin mactime_plugin.so { enable } } logging { @@ -81,7 +86,7 @@ type VppInstance struct { container *Container additionalConfig []Stanza connection *core.Connection - apiChannel api.Channel + apiStream api.Stream cpus []int } @@ -106,6 +111,11 @@ func (vpp *VppInstance) getEtcDir() string { } func (vpp *VppInstance) start() error { + maxReconnectAttempts := 3 + // Replace default logger in govpp with our own + govppLogger := logrus.New() + govppLogger.SetOutput(io.MultiWriter(vpp.getSuite().logger.Writer(), GinkgoWriter)) + core.SetLogger(govppLogger) // Create folders containerWorkDir := vpp.container.getContainerWorkDir() @@ -136,6 +146,8 @@ func (vpp *VppInstance) start() error { vpp.getSuite().log("starting vpp") if *isVppDebug { + // default = 3; VPP will timeout while debugging if there are not enough attempts + maxReconnectAttempts = 5000 sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGQUIT) cont := make(chan bool, 1) @@ -160,10 +172,10 @@ func (vpp *VppInstance) start() error { sockAddress := vpp.container.getHostWorkDir() + defaultApiSocketFilePath conn, connEv, err := govpp.AsyncConnect( sockAddress, - core.DefaultMaxReconnectAttempts, + maxReconnectAttempts, core.DefaultReconnectInterval) if err != nil { - fmt.Println("async connect error: ", err) + vpp.getSuite().log("async connect error: " + fmt.Sprint(err)) return err } vpp.connection = conn @@ -171,24 +183,19 @@ func (vpp *VppInstance) start() error { // ... wait for Connected event e := <-connEv if e.State != core.Connected { - fmt.Println("connecting to VPP failed: ", e.Error) + vpp.getSuite().log("connecting to VPP failed: " + fmt.Sprint(e.Error)) } - // ... check compatibility of used messages - ch, err := conn.NewAPIChannel() + ch, err := conn.NewStream( + context.Background(), + core.WithRequestSize(50), + core.WithReplySize(50), + core.WithReplyTimeout(time.Second*5)) if err != nil { - fmt.Println("creating channel failed: ", err) + vpp.getSuite().log("creating stream failed: " + fmt.Sprint(err)) return err } - if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil { - fmt.Println("compatibility error: ", err) - return err - } - if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil { - fmt.Println("compatibility error: ", err) - return err - } - vpp.apiChannel = ch + vpp.apiStream = ch return nil } @@ -236,31 +243,50 @@ func (vpp *VppInstance) waitForApp(appName string, timeout int) { func (vpp *VppInstance) createAfPacket( veth *NetInterface, ) (interface_types.InterfaceIndex, error) { - createReq := &af_packet.AfPacketCreateV2{ + createReq := &af_packet.AfPacketCreateV3{ + Mode: 1, UseRandomHwAddr: true, HostIfName: veth.Name(), + Flags: af_packet.AfPacketFlags(11), } if veth.hwAddress != (MacAddress{}) { createReq.UseRandomHwAddr = false createReq.HwAddr = veth.hwAddress } - createReply := &af_packet.AfPacketCreateV2Reply{} vpp.getSuite().log("create af-packet interface " + veth.Name()) - if err := vpp.apiChannel.SendRequest(createReq).ReceiveReply(createReply); err != nil { + if err := vpp.apiStream.SendMsg(createReq); err != nil { + vpp.getSuite().hstFail() + return 0, err + } + replymsg, err := vpp.apiStream.RecvMsg() + if err != nil { return 0, err } - veth.index = createReply.SwIfIndex + reply := replymsg.(*af_packet.AfPacketCreateV3Reply) + err = api.RetvalToVPPApiError(reply.Retval) + if err != nil { + return 0, err + } + + veth.index = reply.SwIfIndex // Set to up upReq := &interfaces.SwInterfaceSetFlags{ SwIfIndex: veth.index, Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP, } - upReply := &interfaces.SwInterfaceSetFlagsReply{} vpp.getSuite().log("set af-packet interface " + veth.Name() + " up") - if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil { + if err := vpp.apiStream.SendMsg(upReq); err != nil { + return 0, err + } + replymsg, err = vpp.apiStream.RecvMsg() + if err != nil { + return 0, err + } + reply2 := replymsg.(*interfaces.SwInterfaceSetFlagsReply) + if err = api.RetvalToVPPApiError(reply2.Retval); err != nil { return 0, err } @@ -279,10 +305,18 @@ func (vpp *VppInstance) createAfPacket( SwIfIndex: veth.index, Prefix: veth.addressWithPrefix(), } - addressReply := &interfaces.SwInterfaceAddDelAddressReply{} vpp.getSuite().log("af-packet interface " + veth.Name() + " add address " + veth.ip4Address) - if err := vpp.apiChannel.SendRequest(addressReq).ReceiveReply(addressReply); err != nil { + if err := vpp.apiStream.SendMsg(addressReq); err != nil { + return 0, err + } + replymsg, err = vpp.apiStream.RecvMsg() + if err != nil { + return 0, err + } + reply3 := replymsg.(*interfaces.SwInterfaceAddDelAddressReply) + err = api.RetvalToVPPApiError(reply3.Retval) + if err != nil { return 0, err } @@ -294,25 +328,41 @@ func (vpp *VppInstance) addAppNamespace( ifx interface_types.InterfaceIndex, namespaceId string, ) error { - req := &session.AppNamespaceAddDelV2{ + req := &session.AppNamespaceAddDelV4{ + IsAdd: true, Secret: secret, SwIfIndex: ifx, NamespaceID: namespaceId, + SockName: defaultApiSocketFilePath, } - reply := &session.AppNamespaceAddDelV2Reply{} vpp.getSuite().log("add app namespace " + namespaceId) - if err := vpp.apiChannel.SendRequest(req).ReceiveReply(reply); err != nil { + if err := vpp.apiStream.SendMsg(req); err != nil { + return err + } + replymsg, err := vpp.apiStream.RecvMsg() + if err != nil { + return err + } + reply := replymsg.(*session.AppNamespaceAddDelV4Reply) + if err = api.RetvalToVPPApiError(reply.Retval); err != nil { return err } sessionReq := &session.SessionEnableDisable{ IsEnable: true, } - sessionReply := &session.SessionEnableDisableReply{} vpp.getSuite().log("enable app namespace " + namespaceId) - if err := vpp.apiChannel.SendRequest(sessionReq).ReceiveReply(sessionReply); err != nil { + if err := vpp.apiStream.SendMsg(sessionReq); err != nil { + return err + } + replymsg, err = vpp.apiStream.RecvMsg() + if err != nil { + return err + } + reply2 := replymsg.(*session.SessionEnableDisableReply) + if err = api.RetvalToVPPApiError(reply2.Retval); err != nil { return err } @@ -327,45 +377,86 @@ func (vpp *VppInstance) createTap( if len(tapId) > 0 { id = tapId[0] } - createTapReq := &tapv2.TapCreateV2{ + createTapReq := &tapv2.TapCreateV3{ ID: id, HostIfNameSet: true, HostIfName: tap.Name(), HostIP4PrefixSet: true, HostIP4Prefix: tap.ip4AddressWithPrefix(), } - createTapReply := &tapv2.TapCreateV2Reply{} vpp.getSuite().log("create tap interface " + tap.Name()) // Create tap interface - if err := vpp.apiChannel.SendRequest(createTapReq).ReceiveReply(createTapReply); err != nil { + if err := vpp.apiStream.SendMsg(createTapReq); err != nil { + return err + } + replymsg, err := vpp.apiStream.RecvMsg() + if err != nil { + return err + } + reply := replymsg.(*tapv2.TapCreateV3Reply) + if err = api.RetvalToVPPApiError(reply.Retval); err != nil { + return err + } + tap.peer.index = reply.SwIfIndex + + // Get name and mac + if err := vpp.apiStream.SendMsg(&interfaces.SwInterfaceDump{ + SwIfIndex: reply.SwIfIndex, + }); err != nil { + return err + } + replymsg, err = vpp.apiStream.RecvMsg() + if err != nil { return err } + ifDetails := replymsg.(*interfaces.SwInterfaceDetails) + tap.peer.name = ifDetails.InterfaceName + tap.peer.hwAddress = ifDetails.L2Address // Add address addAddressReq := &interfaces.SwInterfaceAddDelAddress{ IsAdd: true, - SwIfIndex: createTapReply.SwIfIndex, + SwIfIndex: reply.SwIfIndex, Prefix: tap.peer.addressWithPrefix(), } - addAddressReply := &interfaces.SwInterfaceAddDelAddressReply{} vpp.getSuite().log("tap interface " + tap.Name() + " add address " + tap.peer.ip4Address) - if err := vpp.apiChannel.SendRequest(addAddressReq).ReceiveReply(addAddressReply); err != nil { + if err := vpp.apiStream.SendMsg(addAddressReq); err != nil { + return err + } + replymsg, err = vpp.apiStream.RecvMsg() + if err != nil { + return err + } + reply2 := replymsg.(*interfaces.SwInterfaceAddDelAddressReply) + if err = api.RetvalToVPPApiError(reply2.Retval); err != nil { return err } // Set interface to up upReq := &interfaces.SwInterfaceSetFlags{ - SwIfIndex: createTapReply.SwIfIndex, + SwIfIndex: reply.SwIfIndex, Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP, } - upReply := &interfaces.SwInterfaceSetFlagsReply{} - vpp.getSuite().log("set tap interface " + tap.Name() + " up") - if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil { + if err := vpp.apiStream.SendMsg(upReq); err != nil { return err } + replymsg, err = vpp.apiStream.RecvMsg() + if err != nil { + return err + } + reply3 := replymsg.(*interfaces.SwInterfaceSetFlagsReply) + if err = api.RetvalToVPPApiError(reply3.Retval); err != nil { + return err + } + + // Get host mac + netIntf, err := net.InterfaceByName(tap.Name()) + if err == nil { + tap.hwAddress, _ = ethernet_types.ParseMacAddress(netIntf.HardwareAddr.String()) + } return nil } @@ -380,7 +471,7 @@ func (vpp *VppInstance) saveLogs() { func (vpp *VppInstance) disconnect() { vpp.connection.Disconnect() - vpp.apiChannel.Close() + vpp.apiStream.Close() } func (vpp *VppInstance) generateCpuConfig() string { @@ -391,6 +482,7 @@ func (vpp *VppInstance) generateCpuConfig() string { } c.newStanza("cpu"). append(fmt.Sprintf("main-core %d", vpp.cpus[0])) + vpp.getSuite().log(fmt.Sprintf("main-core %d", vpp.cpus[0])) workers := vpp.cpus[1:] if len(workers) > 0 { @@ -401,6 +493,7 @@ func (vpp *VppInstance) generateCpuConfig() string { s = s + fmt.Sprintf("%d", workers[i]) } c.append(fmt.Sprintf("corelist-workers %s", s)) + vpp.getSuite().log("corelist-workers " + s) } return c.close().toString() } diff --git a/extras/strongswan/vpp_sswan/Makefile b/extras/strongswan/vpp_sswan/Makefile index 254b90b3b09..85fb506f346 100644 --- a/extras/strongswan/vpp_sswan/Makefile +++ b/extras/strongswan/vpp_sswan/Makefile @@ -76,8 +76,8 @@ install-swan: echo "SSWAN not downloaded, please run "make" or "make pull-swan" first." ; \ exit 1 ; \ fi - cd ${SWANDIR} && make -j$(nproc) - cd ${SWANDIR} && sudo make install + cd ${SWANDIR} && $(MAKE) -j$(nproc) + cd ${SWANDIR} && sudo $(MAKE) install # check if VPP is installed ifneq ($(shell test "$(shell ldconfig -p | grep vppinfra.so | awk 'NR==1{print $$1;}')" && echo "yes"), yes) diff --git a/extras/vpp_stats_fs/stats_fs.go b/extras/vpp_stats_fs/stats_fs.go index 80c15096234..7b0f1ec193f 100644 --- a/extras/vpp_stats_fs/stats_fs.go +++ b/extras/vpp_stats_fs/stats_fs.go @@ -132,7 +132,7 @@ func getCounterContent(index uint32, client *statsclient.StatsClient) (content s return content, fs.OK } -//The dirNode structure represents directories +// The dirNode structure represents directories type dirNode struct { fs.Inode client *statsclient.StatsClient @@ -175,7 +175,7 @@ func (dn *dirNode) Opendir(ctx context.Context) syscall.Errno { return status } -//The statNode structure represents counters +// The statNode structure represents counters type statNode struct { fs.Inode client *statsclient.StatsClient @@ -192,7 +192,7 @@ func (fh *statNode) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.Attr return 0 } -//When a file is opened, the correpsonding counter value is dumped and a file handle is created +// When a file is opened, the correpsonding counter value is dumped and a file handle is created func (sn *statNode) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) { content, status := getCounterContent(sn.index, sn.client) if status == syscall.ENOENT { @@ -220,7 +220,7 @@ func (fh *statFH) Read(ctx context.Context, data []byte, off int64) (fuse.ReadRe return fuse.ReadResultData(fh.data[off:end]), fs.OK } -//NewStatsFileSystem creates the fs for the stat segment. +// NewStatsFileSystem creates the fs for the stat segment. func NewStatsFileSystem(sc *statsclient.StatsClient) (root fs.InodeEmbedder, err error) { return &dirNode{client: sc}, nil } diff --git a/src/cmake/platform/octeon9.cmake b/src/cmake/platform/octeon9.cmake new file mode 100644 index 00000000000..46ca7dfa64a --- /dev/null +++ b/src/cmake/platform/octeon9.cmake @@ -0,0 +1,4 @@ + +set(VPP_PLATFORM_CACHE_LINE_SIZE 128) +set(VPP_PLATFORM_MARCH_FLAGS -march=armv8.2-a+crc+crypto) +set(VPP_PLATFORM_BUFFER_ALIGN 128) diff --git a/src/plugins/acl/acl_test.c b/src/plugins/acl/acl_test.c index 8404689dc06..98803a916cb 100644 --- a/src/plugins/acl/acl_test.c +++ b/src/plugins/acl/acl_test.c @@ -114,7 +114,7 @@ static void vl_api_acl_interface_list_details_t_handler int i; vat_main_t * vam = acl_test_main.vat_main; u8 *out = 0; - vl_api_acl_interface_list_details_t_endian(mp); + vl_api_acl_interface_list_details_t_endian (mp, 0 /* from network */); out = format(out, "sw_if_index: %d, count: %d, n_input: %d\n", mp->sw_if_index, mp->count, mp->n_input); out = format(out, " input "); for(i=0; i<mp->count; i++) { @@ -141,7 +141,8 @@ static void vl_api_acl_interface_etype_whitelist_details_t_handler int i; vat_main_t * vam = acl_test_main.vat_main; u8 *out = 0; - vl_api_acl_interface_etype_whitelist_details_t_endian(mp); + vl_api_acl_interface_etype_whitelist_details_t_endian ( + mp, 0 /* from network */); out = format(out, "sw_if_index: %d, count: %d, n_input: %d\n", mp->sw_if_index, mp->count, mp->n_input); out = format(out, " input "); for(i=0; i<mp->count; i++) { @@ -173,15 +174,15 @@ vl_api_acl_rule_t_pretty_format (u8 *out, vl_api_acl_rule_t * a) inet_ntop(af, &a->src_prefix.address.un, (void *)src, sizeof(src)); inet_ntop(af, &a->dst_prefix.address.un, (void *)dst, sizeof(dst)); - out = format(out, "%s action %d src %s/%d dst %s/%d proto %d sport %d-%d dport %d-%d tcpflags %d mask %d", - a->src_prefix.address.af ? "ipv6" : "ipv4", a->is_permit, - src, a->src_prefix.len, - dst, a->dst_prefix.len, - a->proto, - a->srcport_or_icmptype_first, a->srcport_or_icmptype_last, - a->dstport_or_icmpcode_first, a->dstport_or_icmpcode_last, - a->tcp_flags_value, a->tcp_flags_mask); - return(out); + out = format (out, + "%s action %d src %s/%d dst %s/%d proto %d sport %d-%d dport " + "%d-%d tcpflags %d mask %d", + a->src_prefix.address.af ? "ipv6" : "ipv4", a->is_permit, src, + a->src_prefix.len, dst, a->dst_prefix.len, a->proto, + a->srcport_or_icmptype_first, a->srcport_or_icmptype_last, + a->dstport_or_icmpcode_first, a->dstport_or_icmpcode_last, + a->tcp_flags_value, a->tcp_flags_mask); + return (out); } @@ -191,9 +192,10 @@ static void vl_api_acl_details_t_handler { int i; vat_main_t * vam = acl_test_main.vat_main; - vl_api_acl_details_t_endian(mp); - u8 *out = 0; - out = format(0, "acl_index: %d, count: %d\n tag {%s}\n", mp->acl_index, mp->count, mp->tag); + vl_api_acl_details_t_endian (mp, 0 /* from network */); + u8 *out = 0; + out = format (0, "acl_index: %d, count: %d\n tag {%s}\n", + mp->acl_index, mp->count, mp->tag); for(i=0; i<mp->count; i++) { out = format(out, " "); out = vl_api_acl_rule_t_pretty_format(out, &mp->r[i]); @@ -225,8 +227,9 @@ static void vl_api_macip_acl_details_t_handler { int i; vat_main_t * vam = acl_test_main.vat_main; - vl_api_macip_acl_details_t_endian(mp); - u8 *out = format(0,"MACIP acl_index: %d, count: %d\n tag {%s}\n", mp->acl_index, mp->count, mp->tag); + vl_api_macip_acl_details_t_endian (mp, 0 /* from network */); + u8 *out = format (0, "MACIP acl_index: %d, count: %d\n tag {%s}\n", + mp->acl_index, mp->count, mp->tag); for(i=0; i<mp->count; i++) { out = format(out, " "); out = vl_api_macip_acl_rule_t_pretty_format(out, &mp->r[i]); diff --git a/src/plugins/builtinurl/builtins.c b/src/plugins/builtinurl/builtins.c index b04e9dd5c7c..6a0975503a7 100644 --- a/src/plugins/builtinurl/builtins.c +++ b/src/plugins/builtinurl/builtins.c @@ -78,16 +78,14 @@ handle_get_interface_stats (hss_url_handler_args_t *args) vnet_interface_main_t *im = &vnm->interface_main; /* Get stats for a single interface via http POST */ - if (args->reqtype == HTTP_REQ_POST) + if (args->req_type == HTTP_REQ_POST) { - trim_path_from_request (args->request, "interface_stats.json"); - /* Find the sw_if_index */ - p = hash_get (im->hw_interface_by_name, args->request); + p = hash_get (im->hw_interface_by_name, args->req_data); if (!p) { s = format (s, "{\"interface_stats\": {[\n"); - s = format (s, " \"name\": \"%s\",", args->request); + s = format (s, " \"name\": \"%s\",", args->req_data); s = format (s, " \"error\": \"%s\"", "UnknownInterface"); s = format (s, "]}\n"); goto out; diff --git a/src/plugins/builtinurl/builtinurl.api b/src/plugins/builtinurl/builtinurl.api index f292fd77a8e..80efa73c725 100644 --- a/src/plugins/builtinurl/builtinurl.api +++ b/src/plugins/builtinurl/builtinurl.api @@ -35,6 +35,7 @@ option version = "1.0.0"; */ autoreply define builtinurl_enable { + option deprecated="incorporated in http_static plugin"; /* Client identifier, set from api_main.my_client_index */ u32 client_index; diff --git a/src/plugins/crypto_native/sha2.c b/src/plugins/crypto_native/sha2.c index 459ce6d8e79..6787f629104 100644 --- a/src/plugins/crypto_native/sha2.c +++ b/src/plugins/crypto_native/sha2.c @@ -118,13 +118,25 @@ sha2_key_add (vnet_crypto_key_t *key, clib_sha2_type_t type) static int probe () { -#if defined(__SHA__) && defined(__x86_64__) +#if defined(__x86_64__) + +#if defined(__SHA__) && defined(__AVX512F__) + if (clib_cpu_supports_sha () && clib_cpu_supports_avx512f ()) + return 30; +#elif defined(__SHA__) && defined(__AVX2__) + if (clib_cpu_supports_sha () && clib_cpu_supports_avx2 ()) + return 20; +#elif defined(__SHA__) if (clib_cpu_supports_sha ()) - return 50; -#elif defined(__ARM_FEATURE_SHA2) + return 10; +#endif + +#elif defined(__aarch64__) +#if defined(__ARM_FEATURE_SHA2) if (clib_cpu_supports_sha2 ()) return 10; #endif +#endif return -1; } diff --git a/src/plugins/dev_iavf/port.c b/src/plugins/dev_iavf/port.c index 982436d9b45..90e81e960c4 100644 --- a/src/plugins/dev_iavf/port.c +++ b/src/plugins/dev_iavf/port.c @@ -42,29 +42,35 @@ iavf_port_vlan_strip_disable (vlib_main_t *vm, vnet_dev_port_t *port) vnet_dev_t *dev = port->dev; iavf_port_t *ap = vnet_dev_get_port_data (port); virtchnl_vlan_caps_t vc; - vnet_dev_rv_t rv; + vnet_dev_rv_t rv = VNET_DEV_ERR_NOT_SUPPORTED; u32 outer, inner; const u32 mask = VIRTCHNL_VLAN_ETHERTYPE_8100; - if ((ap->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_VLAN_V2) == 0) - return iavf_vc_op_disable_vlan_stripping (vm, dev); + if (ap->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_VLAN_V2) + { + if ((rv = iavf_vc_op_get_offload_vlan_v2_caps (vm, dev, &vc))) + return rv; - if ((rv = iavf_vc_op_get_offload_vlan_v2_caps (vm, dev, &vc))) - return rv; + outer = vc.offloads.stripping_support.outer; + inner = vc.offloads.stripping_support.inner; - outer = vc.offloads.stripping_support.outer; - inner = vc.offloads.stripping_support.inner; + outer = outer & VIRTCHNL_VLAN_TOGGLE ? outer & mask : 0; + inner = inner & VIRTCHNL_VLAN_TOGGLE ? inner & mask : 0; - outer = outer & VIRTCHNL_VLAN_TOGGLE ? outer & mask : 0; - inner = inner & VIRTCHNL_VLAN_TOGGLE ? inner & mask : 0; + virtchnl_vlan_setting_t vs = { + .vport_id = ap->vsi_id, + .outer_ethertype_setting = outer, + .inner_ethertype_setting = inner, + }; - virtchnl_vlan_setting_t vs = { - .vport_id = ap->vsi_id, - .outer_ethertype_setting = outer, - .inner_ethertype_setting = inner, - }; + if ((rv = iavf_vc_op_disable_vlan_stripping_v2 (vm, dev, &vs))) + return rv; + } - return iavf_vc_op_disable_vlan_stripping_v2 (vm, dev, &vs); + if (ap->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_VLAN) + return iavf_vc_op_disable_vlan_stripping (vm, dev); + + return rv; } vnet_dev_rv_t @@ -275,7 +281,12 @@ iavf_port_init (vlib_main_t *vm, vnet_dev_port_t *port) u64_bit_set (&ap->intr_mode_per_rxq_bitmap, q->queue_id, 1); if ((rv = iavf_port_vlan_strip_disable (vm, port))) - return rv; + { + if (rv == VNET_DEV_ERR_NOT_SUPPORTED) + log_warn (port->dev, "device doesn't support vlan stripping"); + else + return rv; + } if ((rv = iavf_port_init_rss (vm, port))) return rv; diff --git a/src/plugins/dev_octeon/CMakeLists.txt b/src/plugins/dev_octeon/CMakeLists.txt index e8abf1a3389..c6271ecdfba 100644 --- a/src/plugins/dev_octeon/CMakeLists.txt +++ b/src/plugins/dev_octeon/CMakeLists.txt @@ -1,7 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright(c) 2022 Cisco Systems, Inc. -if (NOT VPP_PLATFORM_NAME STREQUAL "octeon10") +if (NOT VPP_PLATFORM_NAME STREQUAL "octeon10" AND NOT VPP_PLATFORM_NAME STREQUAL "octeon9") return() endif() @@ -21,6 +21,10 @@ endif() include_directories (${OCTEON_ROC_DIR}/) +if (VPP_PLATFORM_NAME STREQUAL "octeon9") + add_compile_definitions(PLATFORM_OCTEON9) +endif() + add_vpp_plugin(dev_octeon SOURCES init.c @@ -31,6 +35,7 @@ add_vpp_plugin(dev_octeon rx_node.c tx_node.c flow.c + counter.c MULTIARCH_SOURCES rx_node.c diff --git a/src/plugins/dev_octeon/counter.c b/src/plugins/dev_octeon/counter.c new file mode 100644 index 00000000000..dd73684c386 --- /dev/null +++ b/src/plugins/dev_octeon/counter.c @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2024 Marvell. + * SPDX-License-Identifier: Apache-2.0 + * https://spdx.org/licenses/Apache-2.0.html + */ + +#include <vnet/vnet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/pci.h> +#include <vnet/dev/counters.h> +#include <dev_octeon/octeon.h> +#include <dev_octeon/common.h> + +VLIB_REGISTER_LOG_CLASS (oct_log, static) = { + .class_name = "oct", + .subclass_name = "counters", +}; + +typedef enum +{ + OCT_PORT_CTR_RX_BYTES, + OCT_PORT_CTR_TX_BYTES, + OCT_PORT_CTR_RX_PACKETS, + OCT_PORT_CTR_TX_PACKETS, + OCT_PORT_CTR_RX_DROPS, + OCT_PORT_CTR_TX_DROPS, + OCT_PORT_CTR_RX_DROP_BYTES, + OCT_PORT_CTR_RX_UCAST, + OCT_PORT_CTR_TX_UCAST, + OCT_PORT_CTR_RX_MCAST, + OCT_PORT_CTR_TX_MCAST, + OCT_PORT_CTR_RX_BCAST, + OCT_PORT_CTR_TX_BCAST, + OCT_PORT_CTR_RX_FCS, + OCT_PORT_CTR_RX_ERR, + OCT_PORT_CTR_RX_DROP_MCAST, + OCT_PORT_CTR_RX_DROP_BCAST, + OCT_PORT_CTR_RX_DROP_L3_MCAST, + OCT_PORT_CTR_RX_DROP_L3_BCAST, +} oct_port_counter_id_t; + +vnet_dev_counter_t oct_port_counters[] = { + VNET_DEV_CTR_RX_BYTES (OCT_PORT_CTR_RX_BYTES), + VNET_DEV_CTR_RX_PACKETS (OCT_PORT_CTR_RX_PACKETS), + VNET_DEV_CTR_RX_DROPS (OCT_PORT_CTR_RX_DROPS), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_DROP_BYTES, RX, BYTES, "drop bytes"), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_UCAST, RX, PACKETS, "unicast"), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_MCAST, RX, PACKETS, "multicast"), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_BCAST, RX, PACKETS, "broadcast"), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_FCS, RX, PACKETS, "fcs"), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_ERR, RX, PACKETS, "error"), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_DROP_MCAST, RX, PACKETS, + "drop multicast"), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_DROP_BCAST, RX, PACKETS, + "drop broadcast"), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_DROP_L3_MCAST, RX, PACKETS, + "drop L3 multicast"), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_RX_DROP_L3_BCAST, RX, PACKETS, + "drop L3 broadcast"), + + VNET_DEV_CTR_TX_BYTES (OCT_PORT_CTR_TX_BYTES), + VNET_DEV_CTR_TX_PACKETS (OCT_PORT_CTR_TX_PACKETS), + VNET_DEV_CTR_TX_DROPS (OCT_PORT_CTR_TX_DROPS), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_TX_UCAST, TX, PACKETS, "unicast"), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_TX_MCAST, TX, PACKETS, "multicast"), + VNET_DEV_CTR_VENDOR (OCT_PORT_CTR_TX_BCAST, TX, PACKETS, "broadcast"), +}; + +typedef enum +{ + OCT_RXQ_CTR_BYTES, + OCT_RXQ_CTR_PKTS, + OCT_RXQ_CTR_DROPS, + OCT_RXQ_CTR_DROP_BYTES, + OCT_RXQ_CTR_ERR, +} oct_rxq_counter_id_t; + +vnet_dev_counter_t oct_rxq_counters[] = { + VNET_DEV_CTR_RX_BYTES (OCT_RXQ_CTR_BYTES), + VNET_DEV_CTR_RX_PACKETS (OCT_RXQ_CTR_PKTS), + VNET_DEV_CTR_RX_DROPS (OCT_RXQ_CTR_DROPS), + VNET_DEV_CTR_VENDOR (OCT_RXQ_CTR_DROP_BYTES, RX, BYTES, "drop bytes"), + VNET_DEV_CTR_VENDOR (OCT_RXQ_CTR_ERR, RX, PACKETS, "error"), +}; + +typedef enum +{ + OCT_TXQ_CTR_BYTES, + OCT_TXQ_CTR_PKTS, + OCT_TXQ_CTR_DROPS, + OCT_TXQ_CTR_DROP_BYTES, +} oct_txq_counter_id_t; + +vnet_dev_counter_t oct_txq_counters[] = { + VNET_DEV_CTR_TX_BYTES (OCT_TXQ_CTR_BYTES), + VNET_DEV_CTR_TX_PACKETS (OCT_TXQ_CTR_PKTS), + VNET_DEV_CTR_TX_DROPS (OCT_TXQ_CTR_DROPS), + VNET_DEV_CTR_VENDOR (OCT_TXQ_CTR_DROP_BYTES, TX, BYTES, "drop bytes"), +}; + +static vnet_dev_rv_t +oct_roc_err (vnet_dev_t *dev, int rv, char *fmt, ...) +{ + u8 *s = 0; + va_list va; + + va_start (va, fmt); + s = va_format (s, fmt, &va); + va_end (va); + + log_err (dev, "%v - ROC error %s (%d)", s, roc_error_msg_get (rv), rv); + + vec_free (s); + return VNET_DEV_ERR_INTERNAL; +} + +void +oct_port_add_counters (vlib_main_t *vm, vnet_dev_port_t *port) +{ + vnet_dev_port_add_counters (vm, port, oct_port_counters, + ARRAY_LEN (oct_port_counters)); + + foreach_vnet_dev_port_rx_queue (rxq, port) + { + vnet_dev_rx_queue_add_counters (vm, rxq, oct_rxq_counters, + ARRAY_LEN (oct_rxq_counters)); + } + + foreach_vnet_dev_port_tx_queue (txq, port) + { + vnet_dev_tx_queue_add_counters (vm, txq, oct_txq_counters, + ARRAY_LEN (oct_txq_counters)); + } +} + +vnet_dev_rv_t +oct_port_get_stats (vlib_main_t *vm, vnet_dev_port_t *port) +{ + vnet_dev_t *dev = port->dev; + oct_device_t *cd = vnet_dev_get_data (dev); + struct roc_nix *nix = cd->nix; + int rrv; + struct roc_nix_stats stats; + + if ((rrv = roc_nix_stats_get (nix, &stats))) + return oct_roc_err (dev, rrv, "roc_nix_stats_get() failed"); + + foreach_vnet_dev_counter (c, port->counter_main) + { + switch (c->user_data) + { + case OCT_PORT_CTR_RX_BYTES: + vnet_dev_counter_value_update (vm, c, stats.rx_octs); + break; + case OCT_PORT_CTR_TX_BYTES: + vnet_dev_counter_value_update (vm, c, stats.tx_octs); + break; + case OCT_PORT_CTR_RX_PACKETS: + vnet_dev_counter_value_update ( + vm, c, stats.rx_ucast + stats.rx_bcast + stats.rx_mcast); + break; + case OCT_PORT_CTR_TX_PACKETS: + vnet_dev_counter_value_update ( + vm, c, stats.tx_ucast + stats.tx_bcast + stats.tx_mcast); + break; + case OCT_PORT_CTR_RX_DROPS: + vnet_dev_counter_value_update (vm, c, stats.rx_drop); + break; + case OCT_PORT_CTR_TX_DROPS: + vnet_dev_counter_value_update (vm, c, stats.tx_drop); + break; + case OCT_PORT_CTR_RX_DROP_BYTES: + vnet_dev_counter_value_update (vm, c, stats.rx_drop_octs); + break; + case OCT_PORT_CTR_RX_UCAST: + vnet_dev_counter_value_update (vm, c, stats.rx_ucast); + break; + case OCT_PORT_CTR_TX_UCAST: + vnet_dev_counter_value_update (vm, c, stats.tx_ucast); + break; + case OCT_PORT_CTR_RX_MCAST: + vnet_dev_counter_value_update (vm, c, stats.rx_mcast); + break; + case OCT_PORT_CTR_TX_MCAST: + vnet_dev_counter_value_update (vm, c, stats.tx_mcast); + break; + case OCT_PORT_CTR_RX_BCAST: + vnet_dev_counter_value_update (vm, c, stats.rx_bcast); + break; + case OCT_PORT_CTR_TX_BCAST: + vnet_dev_counter_value_update (vm, c, stats.tx_bcast); + break; + case OCT_PORT_CTR_RX_FCS: + vnet_dev_counter_value_update (vm, c, stats.rx_fcs); + break; + case OCT_PORT_CTR_RX_ERR: + vnet_dev_counter_value_update (vm, c, stats.rx_err); + break; + case OCT_PORT_CTR_RX_DROP_MCAST: + vnet_dev_counter_value_update (vm, c, stats.rx_drop_mcast); + break; + case OCT_PORT_CTR_RX_DROP_BCAST: + vnet_dev_counter_value_update (vm, c, stats.rx_drop_bcast); + break; + case OCT_PORT_CTR_RX_DROP_L3_MCAST: + vnet_dev_counter_value_update (vm, c, stats.rx_drop_l3_mcast); + break; + case OCT_PORT_CTR_RX_DROP_L3_BCAST: + vnet_dev_counter_value_update (vm, c, stats.rx_drop_l3_bcast); + break; + default: + ASSERT (0); + } + } + + return VNET_DEV_OK; +} + +vnet_dev_rv_t +oct_rxq_get_stats (vlib_main_t *vm, vnet_dev_port_t *port, + vnet_dev_rx_queue_t *rxq) +{ + oct_rxq_t *crq = vnet_dev_get_rx_queue_data (rxq); + struct roc_nix_stats_queue qstats; + vnet_dev_t *dev = port->dev; + oct_device_t *cd = vnet_dev_get_data (dev); + struct roc_nix *nix = cd->nix; + int rrv; + + if ((rrv = roc_nix_stats_queue_get (nix, crq->rq.qid, 1, &qstats))) + return oct_roc_err (dev, rrv, "roc_nix_stats_queue_get() failed"); + + foreach_vnet_dev_counter (c, rxq->counter_main) + { + switch (c->user_data) + { + case OCT_RXQ_CTR_BYTES: + vnet_dev_counter_value_update (vm, c, qstats.rx_octs); + break; + case OCT_RXQ_CTR_PKTS: + vnet_dev_counter_value_update (vm, c, qstats.rx_pkts); + break; + case OCT_RXQ_CTR_DROPS: + vnet_dev_counter_value_update (vm, c, qstats.rx_drop_pkts); + break; + case OCT_RXQ_CTR_DROP_BYTES: + vnet_dev_counter_value_update (vm, c, qstats.rx_drop_octs); + break; + case OCT_RXQ_CTR_ERR: + vnet_dev_counter_value_update (vm, c, qstats.rx_error_pkts); + break; + default: + ASSERT (0); + } + } + + return VNET_DEV_OK; +} + +vnet_dev_rv_t +oct_txq_get_stats (vlib_main_t *vm, vnet_dev_port_t *port, + vnet_dev_tx_queue_t *txq) +{ + oct_txq_t *ctq = vnet_dev_get_tx_queue_data (txq); + struct roc_nix_stats_queue qstats; + vnet_dev_t *dev = port->dev; + oct_device_t *cd = vnet_dev_get_data (dev); + struct roc_nix *nix = cd->nix; + int rrv; + + if ((rrv = roc_nix_stats_queue_get (nix, ctq->sq.qid, 0, &qstats))) + return oct_roc_err (dev, rrv, "roc_nix_stats_queue_get() failed"); + + foreach_vnet_dev_counter (c, txq->counter_main) + { + switch (c->user_data) + { + case OCT_TXQ_CTR_BYTES: + vnet_dev_counter_value_update (vm, c, qstats.tx_octs); + break; + case OCT_TXQ_CTR_PKTS: + vnet_dev_counter_value_update (vm, c, qstats.tx_pkts); + break; + case OCT_TXQ_CTR_DROPS: + vnet_dev_counter_value_update (vm, c, qstats.tx_drop_pkts); + break; + case OCT_TXQ_CTR_DROP_BYTES: + vnet_dev_counter_value_update (vm, c, qstats.tx_drop_octs); + break; + default: + ASSERT (0); + } + } + + return VNET_DEV_OK; +} + +void +oct_port_clear_counters (vlib_main_t *vm, vnet_dev_port_t *port) +{ + vnet_dev_t *dev = port->dev; + oct_device_t *cd = vnet_dev_get_data (dev); + struct roc_nix *nix = cd->nix; + int rrv; + + if ((rrv = roc_nix_stats_reset (nix))) + oct_roc_err (dev, rrv, "roc_nix_stats_reset() failed"); +} + +void +oct_rxq_clear_counters (vlib_main_t *vm, vnet_dev_rx_queue_t *rxq) +{ + oct_rxq_t *crq = vnet_dev_get_rx_queue_data (rxq); + vnet_dev_t *dev = rxq->port->dev; + oct_device_t *cd = vnet_dev_get_data (dev); + struct roc_nix *nix = cd->nix; + int rrv; + + if ((rrv = roc_nix_stats_queue_reset (nix, crq->rq.qid, 1))) + oct_roc_err (dev, rrv, + "roc_nix_stats_queue_reset() failed for rx queue %u", + rxq->queue_id); +} + +void +oct_txq_clear_counters (vlib_main_t *vm, vnet_dev_tx_queue_t *txq) +{ + oct_txq_t *ctq = vnet_dev_get_tx_queue_data (txq); + vnet_dev_t *dev = txq->port->dev; + oct_device_t *cd = vnet_dev_get_data (dev); + struct roc_nix *nix = cd->nix; + int rrv; + + if ((rrv = roc_nix_stats_queue_reset (nix, ctq->sq.qid, 0))) + oct_roc_err (dev, rrv, + "roc_nix_stats_queue_reset() failed for tx queue %u", + txq->queue_id); +} diff --git a/src/plugins/dev_octeon/flow.c b/src/plugins/dev_octeon/flow.c index 1c367a036ab..35aabde76a7 100644 --- a/src/plugins/dev_octeon/flow.c +++ b/src/plugins/dev_octeon/flow.c @@ -46,6 +46,8 @@ VLIB_REGISTER_LOG_CLASS (oct_log, static) = { (f->type == VNET_FLOW_TYPE_IP4_GTPC) || \ (f->type == VNET_FLOW_TYPE_IP4_GTPU)) +#define FLOW_IS_GENERIC_TYPE(f) (f->type == VNET_FLOW_TYPE_GENERIC) + #define OCT_FLOW_UNSUPPORTED_ACTIONS(f) \ ((f->actions == VNET_FLOW_ACTION_BUFFER_ADVANCE) || \ (f->actions == VNET_FLOW_ACTION_REDIRECT_TO_NODE)) @@ -71,6 +73,9 @@ VLIB_REGISTER_LOG_CLASS (oct_log, static) = { _ (62, FLOW_KEY_TYPE_L3_DST, "l3-dst-only") \ _ (63, FLOW_KEY_TYPE_L3_SRC, "l3-src-only") +#define GTPU_PORT 2152 +#define VXLAN_PORT 4789 + typedef struct { u16 src_port; @@ -87,6 +92,27 @@ typedef struct u32 teid; } gtpu_header_t; +typedef struct +{ + u8 layer; + u16 nxt_proto; + vnet_dev_port_t *port; + struct roc_npc_item_info *items; + struct + { + u8 *spec; + u8 *mask; + u16 off; + } oct_drv; + struct + { + u8 *spec; + u8 *mask; + u16 off; + u16 len; + } generic; +} oct_flow_parse_state; + static void oct_flow_convert_rss_types (u64 *key, u64 rss_types) { @@ -183,6 +209,320 @@ oct_flow_rule_create (vnet_dev_port_t *port, struct roc_npc_action *actions, return VNET_DEV_OK; } +static int +oct_parse_l2 (oct_flow_parse_state *pst) +{ + struct roc_npc_flow_item_eth *eth_spec = + (struct roc_npc_flow_item_eth *) &pst->oct_drv.spec[pst->oct_drv.off]; + struct roc_npc_flow_item_eth *eth_mask = + (struct roc_npc_flow_item_eth *) &pst->oct_drv.mask[pst->oct_drv.off]; + ethernet_header_t *eth_hdr_mask = + (ethernet_header_t *) &pst->generic.mask[pst->generic.off]; + ethernet_header_t *eth_hdr = + (ethernet_header_t *) &pst->generic.spec[pst->generic.off]; + u16 tpid, etype; + + tpid = etype = clib_net_to_host_u16 (eth_hdr->type); + clib_memcpy_fast (eth_spec, eth_hdr, sizeof (ethernet_header_t)); + clib_memcpy_fast (eth_mask, eth_hdr_mask, sizeof (ethernet_header_t)); + eth_spec->has_vlan = 0; + + pst->items[pst->layer].spec = (void *) eth_spec; + pst->items[pst->layer].mask = (void *) eth_mask; + pst->items[pst->layer].size = sizeof (ethernet_header_t); + pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_ETH; + pst->generic.off += sizeof (ethernet_header_t); + pst->oct_drv.off += sizeof (struct roc_npc_flow_item_eth); + pst->layer++; + + /* Parse VLAN Tags if any */ + struct roc_npc_flow_item_vlan *vlan_spec = + (struct roc_npc_flow_item_vlan *) &pst->oct_drv.spec[pst->oct_drv.off]; + struct roc_npc_flow_item_vlan *vlan_mask = + (struct roc_npc_flow_item_vlan *) &pst->oct_drv.mask[pst->oct_drv.off]; + ethernet_vlan_header_t *vlan_hdr, *vlan_hdr_mask; + u8 vlan_cnt = 0; + + while (tpid == ETHERNET_TYPE_DOT1AD || tpid == ETHERNET_TYPE_VLAN) + { + if (pst->generic.off >= pst->generic.len) + break; + + vlan_hdr = + (ethernet_vlan_header_t *) &pst->generic.spec[pst->generic.off]; + vlan_hdr_mask = + (ethernet_vlan_header_t *) &pst->generic.mask[pst->generic.off]; + tpid = etype = clib_net_to_host_u16 (vlan_hdr->type); + clib_memcpy (&vlan_spec[vlan_cnt], vlan_hdr, + sizeof (ethernet_vlan_header_t)); + clib_memcpy (&vlan_mask[vlan_cnt], vlan_hdr_mask, + sizeof (ethernet_vlan_header_t)); + pst->items[pst->layer].spec = (void *) &vlan_spec[vlan_cnt]; + pst->items[pst->layer].mask = (void *) &vlan_mask[vlan_cnt]; + pst->items[pst->layer].size = sizeof (ethernet_vlan_header_t); + pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_VLAN; + pst->generic.off += sizeof (ethernet_vlan_header_t); + pst->oct_drv.off += sizeof (struct roc_npc_flow_item_vlan); + pst->layer++; + vlan_cnt++; + } + + /* Inner most vlan tag */ + if (vlan_cnt) + vlan_spec[vlan_cnt - 1].has_more_vlan = 0; + + pst->nxt_proto = etype; + return 0; +} + +static int +oct_parse_l3 (oct_flow_parse_state *pst) +{ + + if (pst->generic.off >= pst->generic.len || pst->nxt_proto == 0) + return 0; + + if (pst->nxt_proto == ETHERNET_TYPE_MPLS) + { + int label_stack_bottom = 0; + do + { + + u8 *mpls_spec = &pst->generic.spec[pst->generic.off]; + u8 *mpls_mask = &pst->generic.mask[pst->generic.off]; + + label_stack_bottom = mpls_spec[2] & 1; + pst->items[pst->layer].spec = (void *) mpls_spec; + pst->items[pst->layer].mask = (void *) mpls_mask; + pst->items[pst->layer].size = sizeof (u32); + pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_MPLS; + pst->generic.off += sizeof (u32); + pst->layer++; + } + while (label_stack_bottom); + + pst->nxt_proto = 0; + return 0; + } + else if (pst->nxt_proto == ETHERNET_TYPE_IP4) + { + ip4_header_t *ip4_spec = + (ip4_header_t *) &pst->generic.spec[pst->generic.off]; + ip4_header_t *ip4_mask = + (ip4_header_t *) &pst->generic.mask[pst->generic.off]; + pst->items[pst->layer].spec = (void *) ip4_spec; + pst->items[pst->layer].mask = (void *) ip4_mask; + pst->items[pst->layer].size = sizeof (ip4_header_t); + pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_IPV4; + pst->generic.off += sizeof (ip4_header_t); + pst->layer++; + pst->nxt_proto = ip4_spec->protocol; + } + else if (pst->nxt_proto == ETHERNET_TYPE_IP6) + { + struct roc_npc_flow_item_ipv6 *ip6_spec = + (struct roc_npc_flow_item_ipv6 *) &pst->oct_drv.spec[pst->oct_drv.off]; + struct roc_npc_flow_item_ipv6 *ip6_mask = + (struct roc_npc_flow_item_ipv6 *) &pst->oct_drv.mask[pst->oct_drv.off]; + ip6_header_t *ip6_hdr_mask = + (ip6_header_t *) &pst->generic.mask[pst->generic.off]; + ip6_header_t *ip6_hdr = + (ip6_header_t *) &pst->generic.spec[pst->generic.off]; + u8 nxt_hdr = ip6_hdr->protocol; + + clib_memcpy (ip6_spec, ip6_hdr, sizeof (ip6_header_t)); + clib_memcpy (ip6_mask, ip6_hdr_mask, sizeof (ip6_header_t)); + pst->items[pst->layer].spec = (void *) ip6_spec; + pst->items[pst->layer].mask = (void *) ip6_mask; + pst->items[pst->layer].size = sizeof (ip6_header_t); + pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_IPV6; + pst->generic.off += sizeof (ip6_header_t); + pst->oct_drv.off += sizeof (struct roc_npc_flow_item_ipv6); + pst->layer++; + + while (nxt_hdr == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS || + nxt_hdr == IP_PROTOCOL_IP6_DESTINATION_OPTIONS || + nxt_hdr == IP_PROTOCOL_IPV6_ROUTE) + { + if (pst->generic.off >= pst->generic.len) + return 0; + + ip6_ext_header_t *ip6_ext_spec = + (ip6_ext_header_t *) &pst->generic.spec[pst->generic.off]; + ip6_ext_header_t *ip6_ext_mask = + (ip6_ext_header_t *) &pst->generic.mask[pst->generic.off]; + nxt_hdr = ip6_ext_spec->next_hdr; + + pst->items[pst->layer].spec = (void *) ip6_ext_spec; + pst->items[pst->layer].mask = (void *) ip6_ext_mask; + pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_IPV6_EXT; + pst->generic.off += ip6_ext_header_len (ip6_ext_spec); + pst->layer++; + } + + if (pst->generic.off >= pst->generic.len) + return 0; + + if (nxt_hdr == IP_PROTOCOL_IPV6_FRAGMENTATION) + { + ip6_frag_hdr_t *ip6_ext_frag_spec = + (ip6_frag_hdr_t *) &pst->generic.spec[pst->generic.off]; + ip6_frag_hdr_t *ip6_ext_frag_mask = + (ip6_frag_hdr_t *) &pst->generic.mask[pst->generic.off]; + + pst->items[pst->layer].spec = (void *) ip6_ext_frag_spec; + pst->items[pst->layer].mask = (void *) ip6_ext_frag_mask; + pst->items[pst->layer].size = sizeof (ip6_frag_hdr_t); + pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_IPV6_FRAG_EXT; + pst->generic.off += sizeof (ip6_frag_hdr_t); + pst->layer++; + } + + pst->nxt_proto = nxt_hdr; + } + /* Unsupported L3. */ + else + return -1; + + return 0; +} + +static int +oct_parse_l4 (oct_flow_parse_state *pst) +{ + + if (pst->generic.off >= pst->generic.len || pst->nxt_proto == 0) + return 0; + +#define _(protocol_t, protocol_value, ltype) \ + if (pst->nxt_proto == protocol_value) \ + \ + { \ + \ + protocol_t *spec = (protocol_t *) &pst->generic.spec[pst->generic.off]; \ + protocol_t *mask = (protocol_t *) &pst->generic.mask[pst->generic.off]; \ + pst->items[pst->layer].spec = spec; \ + pst->items[pst->layer].mask = mask; \ + \ + pst->items[pst->layer].size = sizeof (protocol_t); \ + \ + pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_##ltype; \ + pst->generic.off += sizeof (protocol_t); \ + pst->layer++; \ + return 0; \ + } + + _ (esp_header_t, IP_PROTOCOL_IPSEC_ESP, ESP) + _ (udp_header_t, IP_PROTOCOL_UDP, UDP) + _ (tcp_header_t, IP_PROTOCOL_TCP, TCP) + _ (sctp_header_t, IP_PROTOCOL_SCTP, SCTP) + _ (icmp46_header_t, IP_PROTOCOL_ICMP, ICMP) + _ (icmp46_header_t, IP_PROTOCOL_ICMP6, ICMP) + _ (igmp_header_t, IP_PROTOCOL_IGMP, IGMP) + _ (gre_header_t, IP_PROTOCOL_GRE, GRE) + + /* Unsupported L4. */ + return -1; +} + +static int +oct_parse_tunnel (oct_flow_parse_state *pst) +{ + if (pst->generic.off >= pst->generic.len) + return 0; + + if (pst->items[pst->layer - 1].type == ROC_NPC_ITEM_TYPE_GRE) + { + gre_header_t *gre_hdr = (gre_header_t *) pst->items[pst->layer - 1].spec; + pst->nxt_proto = clib_net_to_host_u16 (gre_hdr->protocol); + goto parse_l3; + } + + else if (pst->items[pst->layer - 1].type == ROC_NPC_ITEM_TYPE_UDP) + { + udp_header_t *udp_h = (udp_header_t *) pst->items[pst->layer - 1].spec; + u16 dport = clib_net_to_host_u16 (udp_h->dst_port); + + if (dport == GTPU_PORT) + { + gtpu_header_t *gtpu_spec = + (gtpu_header_t *) &pst->generic.spec[pst->generic.off]; + gtpu_header_t *gtpu_mask = + (gtpu_header_t *) &pst->generic.mask[pst->generic.off]; + pst->items[pst->layer].spec = (void *) gtpu_spec; + pst->items[pst->layer].mask = (void *) gtpu_mask; + pst->items[pst->layer].size = sizeof (gtpu_header_t); + pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_GTPU; + pst->generic.off += sizeof (gtpu_header_t); + pst->layer++; + pst->nxt_proto = 0; + return 0; + } + else if (dport == VXLAN_PORT) + { + vxlan_header_t *vxlan_spec = + (vxlan_header_t *) &pst->generic.spec[pst->generic.off]; + vxlan_header_t *vxlan_mask = + (vxlan_header_t *) &pst->generic.spec[pst->generic.off]; + pst->items[pst->layer].spec = (void *) vxlan_spec; + pst->items[pst->layer].mask = (void *) vxlan_mask; + pst->items[pst->layer].size = sizeof (vxlan_header_t); + pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_VXLAN; + pst->generic.off += sizeof (vxlan_header_t); + pst->layer++; + pst->nxt_proto = 0; + goto parse_l2; + } + } + /* No supported Tunnel detected. */ + else + { + log_err (pst->port->dev, + "Partially parsed till offset %u, not able to parse further", + pst->generic.off); + return 0; + } +parse_l2: + if (oct_parse_l2 (pst)) + return -1; +parse_l3: + if (oct_parse_l3 (pst)) + return -1; + + return oct_parse_l4 (pst); +} + +static vnet_dev_rv_t +oct_flow_generic_pattern_parse (oct_flow_parse_state *pst) +{ + + if (oct_parse_l2 (pst)) + goto err; + + if (oct_parse_l3 (pst)) + goto err; + + if (oct_parse_l4 (pst)) + goto err; + + if (oct_parse_tunnel (pst)) + goto err; + + if (pst->generic.off < pst->generic.len) + { + log_err (pst->port->dev, + "Partially parsed till offset %u, not able to parse further", + pst->generic.off); + goto err; + } + + pst->items[pst->layer].type = ROC_NPC_ITEM_TYPE_END; + return VNET_DEV_OK; + +err: + return VNET_DEV_ERR_NOT_SUPPORTED; +} + static vnet_dev_rv_t oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, uword *private_data) @@ -196,6 +536,7 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, struct roc_npc_action_queue conf = {}; struct roc_npc_action_mark mark = {}; struct roc_npc *npc = &oct_port->npc; + u8 *flow_spec = 0, *flow_mask = 0; vnet_dev_rv_t rv = VNET_DEV_OK; int layer = 0, index = 0; u16 *queues = NULL; @@ -203,6 +544,45 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, u8 proto = 0; u16 action = 0; + if (FLOW_IS_GENERIC_TYPE (flow)) + { + u8 drv_item_spec[1024] = { 0 }, drv_item_mask[1024] = { 0 }; + unformat_input_t input; + int rc; + + unformat_init_string ( + &input, (const char *) flow->generic.pattern.spec, + strlen ((const char *) flow->generic.pattern.spec)); + unformat_user (&input, unformat_hex_string, &flow_spec); + unformat_free (&input); + + unformat_init_string ( + &input, (const char *) flow->generic.pattern.mask, + strlen ((const char *) flow->generic.pattern.mask)); + unformat_user (&input, unformat_hex_string, &flow_mask); + unformat_free (&input); + + oct_flow_parse_state pst = { + .nxt_proto = 0, + .port = port, + .items = item_info, + .oct_drv = { .spec = drv_item_spec, .mask = drv_item_mask }, + .generic = { .spec = flow_spec, + .mask = flow_mask, + .len = vec_len (flow_spec) }, + }; + + rc = oct_flow_generic_pattern_parse (&pst); + if (rc) + { + vec_free (flow_spec); + vec_free (flow_mask); + return VNET_DEV_ERR_NOT_SUPPORTED; + } + + goto parse_flow_actions; + } + if (FLOW_IS_ETHERNET_CLASS (flow)) { ethernet_header_t eth_spec = { .type = clib_host_to_net_u16 ( @@ -357,6 +737,7 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, end_item_info: item_info[layer].type = ROC_NPC_ITEM_TYPE_END; +parse_flow_actions: if (flow->actions & VNET_FLOW_ACTION_REDIRECT_TO_QUEUE) { conf.index = flow->redirect_queue; @@ -422,6 +803,11 @@ end_item_info: if (queues) clib_mem_free (queues); + if (flow_spec) + vec_free (flow_spec); + if (flow_mask) + vec_free (flow_mask); + return rv; } diff --git a/src/plugins/dev_octeon/format.c b/src/plugins/dev_octeon/format.c index e624b84f54e..d0f53013d99 100644 --- a/src/plugins/dev_octeon/format.c +++ b/src/plugins/dev_octeon/format.c @@ -25,7 +25,7 @@ format_oct_nix_rx_cqe_desc (u8 *s, va_list *args) typeof (d->sg0) *sg0 = &d->sg0; typeof (d->sg0) *sg1 = &d->sg1; - s = format (s, "hdr: cqe_type %u nude %u q %u tag 0x%x", h->cqe_type, + s = format (s, "hdr: cqe_type %u nude %u qid %u tag 0x%x", h->cqe_type, h->node, h->q, h->tag); s = format (s, "\n%Uparse:", format_white_space, indent); #define _(n, f) s = format (s, " " #n " " f, p->n) diff --git a/src/plugins/dev_octeon/init.c b/src/plugins/dev_octeon/init.c index 8c5ed95b062..87ed8317277 100644 --- a/src/plugins/dev_octeon/init.c +++ b/src/plugins/dev_octeon/init.c @@ -51,7 +51,8 @@ static struct } _ (0xa063, RVU_PF, "Marvell Octeon Resource Virtualization Unit PF"), - _ (0xa0f8, RVU_VF, "Marvell Octeon Resource Virtualization Unit VF"), + _ (0xa064, RVU_VF, "Marvell Octeon Resource Virtualization Unit VF"), + _ (0xa0f8, LBK_VF, "Marvell Octeon Loopback Unit VF"), _ (0xa0f7, SDP_VF, "Marvell Octeon System DPI Packet Interface Unit VF"), _ (0xa0f3, CPT_VF, "Marvell Octeon Cryptographic Accelerator Unit VF"), #undef _ @@ -140,6 +141,7 @@ oct_init_nix (vlib_main_t *vm, vnet_dev_t *dev) .config_change_validate = oct_port_cfg_change_validate, .format_status = format_oct_port_status, .format_flow = format_oct_port_flow, + .clear_counters = oct_port_clear_counters, }, .data_size = sizeof (oct_port_t), .initial_data = &oct_port, @@ -158,6 +160,7 @@ oct_init_nix (vlib_main_t *vm, vnet_dev_t *dev) .alloc = oct_rx_queue_alloc, .free = oct_rx_queue_free, .format_info = format_oct_rxq_info, + .clear_counters = oct_rxq_clear_counters, }, }, .tx_queue = { @@ -172,6 +175,7 @@ oct_init_nix (vlib_main_t *vm, vnet_dev_t *dev) .alloc = oct_tx_queue_alloc, .free = oct_tx_queue_free, .format_info = format_oct_txq_info, + .clear_counters = oct_txq_clear_counters, }, }, }; @@ -244,6 +248,7 @@ oct_init (vlib_main_t *vm, vnet_dev_t *dev) { case OCT_DEVICE_TYPE_RVU_PF: case OCT_DEVICE_TYPE_RVU_VF: + case OCT_DEVICE_TYPE_LBK_VF: case OCT_DEVICE_TYPE_SDP_VF: return oct_init_nix (vm, dev); diff --git a/src/plugins/dev_octeon/octeon.h b/src/plugins/dev_octeon/octeon.h index 92ec953ed23..a87a5e3e1ed 100644 --- a/src/plugins/dev_octeon/octeon.h +++ b/src/plugins/dev_octeon/octeon.h @@ -12,6 +12,12 @@ #include <vnet/flow/flow.h> #include <vnet/udp/udp.h> #include <vnet/ipsec/esp.h> +#include <vnet/ethernet/packet.h> +#include <vnet/ip/ip_packet.h> +#include <vnet/ip/icmp46_packet.h> +#include <vnet/ip/igmp_packet.h> +#include <vnet/gre/packet.h> +#include <vxlan/vxlan.h> #include <base/roc_api.h> #include <dev_octeon/hw_defs.h> @@ -22,6 +28,7 @@ typedef enum OCT_DEVICE_TYPE_UNKNOWN = 0, OCT_DEVICE_TYPE_RVU_PF, OCT_DEVICE_TYPE_RVU_VF, + OCT_DEVICE_TYPE_LBK_VF, OCT_DEVICE_TYPE_SDP_VF, OCT_DEVICE_TYPE_CPT_VF, } __clib_packed oct_device_type_t; @@ -140,6 +147,17 @@ vnet_dev_rv_t oct_flow_validate_params (vlib_main_t *, vnet_dev_port_t *, vnet_dev_rv_t oct_flow_query (vlib_main_t *, vnet_dev_port_t *, u32, uword, u64 *); +/* counter.c */ +void oct_port_add_counters (vlib_main_t *, vnet_dev_port_t *); +void oct_port_clear_counters (vlib_main_t *, vnet_dev_port_t *); +void oct_rxq_clear_counters (vlib_main_t *, vnet_dev_rx_queue_t *); +void oct_txq_clear_counters (vlib_main_t *, vnet_dev_tx_queue_t *); +vnet_dev_rv_t oct_port_get_stats (vlib_main_t *, vnet_dev_port_t *); +vnet_dev_rv_t oct_rxq_get_stats (vlib_main_t *, vnet_dev_port_t *, + vnet_dev_rx_queue_t *); +vnet_dev_rv_t oct_txq_get_stats (vlib_main_t *, vnet_dev_port_t *, + vnet_dev_tx_queue_t *); + #define log_debug(dev, f, ...) \ vlib_log (VLIB_LOG_LEVEL_DEBUG, oct_log.class, "%U: " f, \ format_vnet_dev_addr, (dev), ##__VA_ARGS__) @@ -162,7 +180,8 @@ vnet_dev_rv_t oct_flow_query (vlib_main_t *, vnet_dev_port_t *, u32, uword, _ (AURA_BATCH_ALLOC_ISSUE_FAIL, aura_batch_alloc_issue_fail, ERROR, \ "aura batch alloc issue failed") \ _ (AURA_BATCH_ALLOC_NOT_READY, aura_batch_alloc_not_ready, ERROR, \ - "aura batch alloc not ready") + "aura batch alloc not ready") \ + _ (MTU_EXCEEDED, mtu_exceeded, ERROR, "mtu exceeded") typedef enum { diff --git a/src/plugins/dev_octeon/port.c b/src/plugins/dev_octeon/port.c index d5f78301adf..a53fa256884 100644 --- a/src/plugins/dev_octeon/port.c +++ b/src/plugins/dev_octeon/port.c @@ -124,6 +124,8 @@ oct_port_init (vlib_main_t *vm, vnet_dev_port_t *port) return rv; } + oct_port_add_counters (vm, port); + return VNET_DEV_OK; } @@ -172,6 +174,21 @@ oct_port_poll (vlib_main_t *vm, vnet_dev_port_t *port) vnet_dev_port_state_changes_t changes = {}; int rrv; + if (oct_port_get_stats (vm, port)) + return; + + foreach_vnet_dev_port_rx_queue (q, port) + { + if (oct_rxq_get_stats (vm, port, q)) + return; + } + + foreach_vnet_dev_port_tx_queue (q, port) + { + if (oct_txq_get_stats (vm, port, q)) + return; + } + if (roc_nix_is_lbk (nix)) { link_info.status = 1; @@ -203,7 +220,8 @@ oct_port_poll (vlib_main_t *vm, vnet_dev_port_t *port) if (cd->speed != link_info.speed) { changes.change.link_speed = 1; - changes.link_speed = link_info.speed; + /* Convert to Kbps */ + changes.link_speed = link_info.speed * 1000; cd->speed = link_info.speed; } @@ -385,7 +403,7 @@ oct_validate_config_promisc_mode (vnet_dev_port_t *port, int enable) oct_device_t *cd = vnet_dev_get_data (dev); struct roc_nix *nix = cd->nix; - if (roc_nix_is_vf_or_sdp (nix)) + if (roc_nix_is_sdp (nix) || roc_nix_is_lbk (nix)) return VNET_DEV_ERR_UNSUPPORTED_DEVICE; return VNET_DEV_OK; @@ -405,6 +423,9 @@ oct_op_config_promisc_mode (vlib_main_t *vm, vnet_dev_port_t *port, int enable) return oct_roc_err (dev, rv, "roc_nix_npc_promisc_ena_dis failed"); } + if (!roc_nix_is_pf (nix)) + return VNET_DEV_OK; + rv = roc_nix_mac_promisc_mode_enable (nix, enable); if (rv) { @@ -416,6 +437,44 @@ oct_op_config_promisc_mode (vlib_main_t *vm, vnet_dev_port_t *port, int enable) return VNET_DEV_OK; } +static vnet_dev_rv_t +oct_port_add_del_eth_addr (vlib_main_t *vm, vnet_dev_port_t *port, + vnet_dev_hw_addr_t *addr, int is_add, + int is_primary) +{ + vnet_dev_t *dev = port->dev; + oct_device_t *cd = vnet_dev_get_data (dev); + struct roc_nix *nix = cd->nix; + vnet_dev_rv_t rv = VNET_DEV_OK; + + i32 rrv; + + if (is_primary) + { + if (is_add) + { + /* Update mac address at NPC */ + rrv = roc_nix_npc_mac_addr_set (nix, (u8 *) addr); + if (rrv) + rv = oct_roc_err (dev, rrv, "roc_nix_npc_mac_addr_set() failed"); + + /* Update mac address at CGX for PFs only */ + if (!roc_nix_is_vf_or_sdp (nix)) + { + rrv = roc_nix_mac_addr_set (nix, (u8 *) addr); + if (rrv) + { + /* Rollback to previous mac address */ + roc_nix_npc_mac_addr_set (nix, + (u8 *) &port->primary_hw_addr); + rv = oct_roc_err (dev, rrv, "roc_nix_mac_addr_set() failed"); + } + } + } + } + return rv; +} + vnet_dev_rv_t oct_port_cfg_change_validate (vlib_main_t *vm, vnet_dev_port_t *port, vnet_dev_port_cfg_change_req_t *req) @@ -465,6 +524,9 @@ oct_port_cfg_change (vlib_main_t *vm, vnet_dev_port_t *port, break; case VNET_DEV_PORT_CFG_CHANGE_PRIMARY_HW_ADDR: + rv = oct_port_add_del_eth_addr (vm, port, &req->addr, + /* is_add */ 1, + /* is_primary */ 1); break; case VNET_DEV_PORT_CFG_ADD_SECONDARY_HW_ADDR: diff --git a/src/plugins/dev_octeon/roc_helper.c b/src/plugins/dev_octeon/roc_helper.c index f10c2cb578b..16e0a871a9d 100644 --- a/src/plugins/dev_octeon/roc_helper.c +++ b/src/plugins/dev_octeon/roc_helper.c @@ -49,6 +49,12 @@ oct_plt_get_thread_index (void) return __os_thread_index; } +static u64 +oct_plt_get_cache_line_size (void) +{ + return CLIB_CACHE_LINE_BYTES; +} + static void oct_drv_physmem_free (vlib_main_t *vm, void *mem) { @@ -178,4 +184,5 @@ oct_plt_init_param_t oct_plt_init_param = { .oct_plt_spinlock_unlock = oct_plt_spinlock_unlock, .oct_plt_spinlock_trylock = oct_plt_spinlock_trylock, .oct_plt_get_thread_index = oct_plt_get_thread_index, + .oct_plt_get_cache_line_size = oct_plt_get_cache_line_size, }; diff --git a/src/plugins/dev_octeon/rx_node.c b/src/plugins/dev_octeon/rx_node.c index 997f1356199..1f8d5d93fa3 100644 --- a/src/plugins/dev_octeon/rx_node.c +++ b/src/plugins/dev_octeon/rx_node.c @@ -165,6 +165,38 @@ oct_rx_batch (vlib_main_t *vm, oct_rx_node_ctx_t *ctx, return n; } +#ifdef PLATFORM_OCTEON9 +static_always_inline u32 +oct_rxq_refill (vlib_main_t *vm, vnet_dev_rx_queue_t *rxq, u16 n_refill) +{ + u32 n_alloc, n_free; + u32 buffer_indices[n_refill]; + vlib_buffer_t *buffers[n_refill]; + u8 bpi = vnet_dev_get_rx_queue_buffer_pool_index (rxq); + oct_rxq_t *crq = vnet_dev_get_rx_queue_data (rxq); + u64 aura = roc_npa_aura_handle_to_aura (crq->aura_handle); + const uint64_t addr = + roc_npa_aura_handle_to_base (crq->aura_handle) + NPA_LF_AURA_OP_FREE0; + + if (n_refill < 256) + return 0; + + n_alloc = vlib_buffer_alloc (vm, buffer_indices, n_refill); + if (PREDICT_FALSE (n_alloc < n_refill)) + goto alloc_fail; + + vlib_get_buffers (vm, buffer_indices, (vlib_buffer_t **) buffers, n_alloc); + + for (n_free = 0; n_free < n_alloc; n_free++) + roc_store_pair ((u64) buffers[n_free], aura, addr); + + return n_alloc; + +alloc_fail: + vlib_buffer_unalloc_to_pool (vm, buffer_indices, n_alloc, bpi); + return 0; +} +#else static_always_inline void oct_rxq_refill_batch (vlib_main_t *vm, u64 lmt_id, u64 addr, oct_npa_lf_aura_batch_free_line_t *lines, u32 *bi, @@ -260,6 +292,7 @@ oct_rxq_refill (vlib_main_t *vm, vnet_dev_rx_queue_t *rxq, u16 n_refill) return n_enq; } +#endif static_always_inline void oct_rx_trace (vlib_main_t *vm, vlib_node_runtime_t *node, diff --git a/src/plugins/dev_octeon/tx_node.c b/src/plugins/dev_octeon/tx_node.c index 0dbf8759d35..0907493814d 100644 --- a/src/plugins/dev_octeon/tx_node.c +++ b/src/plugins/dev_octeon/tx_node.c @@ -22,13 +22,54 @@ typedef struct u32 n_tx_bytes; u32 n_drop; vlib_buffer_t *drop[VLIB_FRAME_SIZE]; + u32 n_exd_mtu; + vlib_buffer_t *exd_mtu[VLIB_FRAME_SIZE]; u32 batch_alloc_not_ready; u32 batch_alloc_issue_fail; + int max_pkt_len; u16 lmt_id; u64 lmt_ioaddr; lmt_line_t *lmt_lines; } oct_tx_ctx_t; +#ifdef PLATFORM_OCTEON9 +static_always_inline u32 +oct_batch_free (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq) +{ + oct_txq_t *ctq = vnet_dev_get_tx_queue_data (txq); + u16 off = ctq->hdr_off; + u64 ah = ctq->aura_handle; + u32 n_freed = 0, n; + + ah = ctq->aura_handle; + + if ((n = roc_npa_aura_op_available (ah)) >= 32) + { + u64 buffers[n]; + u32 bi[n]; + + n_freed = roc_npa_aura_op_bulk_alloc (ah, buffers, n, 0, 1); + vlib_get_buffer_indices_with_offset (vm, (void **) &buffers, bi, n_freed, + off); + vlib_buffer_free_no_next (vm, bi, n_freed); + } + + return n_freed; +} + +static_always_inline void +oct_lmt_copy (void *lmt_addr, u64 io_addr, void *desc, u64 dwords) +{ + u64 lmt_status; + + do + { + roc_lmt_mov_seg (lmt_addr, desc, dwords); + lmt_status = roc_lmt_submit_ldeor (io_addr); + } + while (lmt_status == 0); +} +#else static_always_inline u32 oct_batch_free (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq) { @@ -130,10 +171,12 @@ oct_batch_free (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq) return n_freed; } +#endif static_always_inline u8 oct_tx_enq1 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vlib_buffer_t *b, - lmt_line_t *line, u32 flags, int simple, int trace) + lmt_line_t *line, u32 flags, int simple, int trace, u32 *n, + u8 *dpl) { u8 n_dwords = 2; u32 total_len = 0; @@ -148,6 +191,17 @@ oct_tx_enq1 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vlib_buffer_t *b, }, }; + if (PREDICT_FALSE (vlib_buffer_length_in_chain (vm, b) > ctx->max_pkt_len)) + { + ctx->exd_mtu[ctx->n_exd_mtu++] = b; + return 0; + } + +#ifdef PLATFORM_OCTEON9 + /* Override line for Octeon9 */ + line = ctx->lmt_lines; +#endif + if (!simple && flags & VLIB_BUFFER_NEXT_PRESENT) { u8 n_tail_segs = 0; @@ -159,7 +213,7 @@ oct_tx_enq1 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vlib_buffer_t *b, tail_segs[n_tail_segs++] = t; if (n_tail_segs > 5) { - ctx->drop[ctx->n_drop++] = t; + ctx->drop[ctx->n_drop++] = b; return 0; } } @@ -228,8 +282,15 @@ oct_tx_enq1 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vlib_buffer_t *b, t->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX]; } +#ifdef PLATFORM_OCTEON9 + oct_lmt_copy (line, ctx->lmt_ioaddr, &d, n_dwords); +#else for (u32 i = 0; i < n_dwords; i++) line->dwords[i] = d.as_u128[i]; +#endif + + *dpl = n_dwords; + *n = *n + 1; return n_dwords; } @@ -239,8 +300,8 @@ oct_tx_enq16 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq, vlib_buffer_t **b, u32 n_pkts, int trace) { u8 dwords_per_line[16], *dpl = dwords_per_line; - u64 lmt_arg, ioaddr, n_lines; - u32 n_left, or_flags_16 = 0; + u64 __attribute__ ((unused)) lmt_arg, ioaddr, n_lines; + u32 n_left, or_flags_16 = 0, n = 0; const u32 not_simple_flags = VLIB_BUFFER_NEXT_PRESENT | VNET_BUFFER_F_OFFLOAD; lmt_line_t *l = ctx->lmt_lines; @@ -248,7 +309,7 @@ oct_tx_enq16 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq, /* Data Store Memory Barrier - outer shareable domain */ asm volatile("dmb oshst" ::: "memory"); - for (n_left = n_pkts; n_left >= 8; n_left -= 8, b += 8, l += 8) + for (n_left = n_pkts; n_left >= 8; n_left -= 8, b += 8) { u32 f0, f1, f2, f3, f4, f5, f6, f7, or_f = 0; vlib_prefetch_buffer_header (b[8], LOAD); @@ -269,49 +330,56 @@ oct_tx_enq16 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq, if ((or_f & not_simple_flags) == 0) { int simple = 1; - oct_tx_enq1 (vm, ctx, b[0], l, f0, simple, trace); - oct_tx_enq1 (vm, ctx, b[1], l + 1, f1, simple, trace); + oct_tx_enq1 (vm, ctx, b[0], l, f0, simple, trace, &n, &dpl[n]); + oct_tx_enq1 (vm, ctx, b[1], l + n, f1, simple, trace, &n, &dpl[n]); vlib_prefetch_buffer_header (b[13], LOAD); - oct_tx_enq1 (vm, ctx, b[2], l + 2, f2, simple, trace); - oct_tx_enq1 (vm, ctx, b[3], l + 3, f3, simple, trace); + oct_tx_enq1 (vm, ctx, b[2], l + n, f2, simple, trace, &n, &dpl[n]); + oct_tx_enq1 (vm, ctx, b[3], l + n, f3, simple, trace, &n, &dpl[n]); vlib_prefetch_buffer_header (b[14], LOAD); - oct_tx_enq1 (vm, ctx, b[4], l + 4, f4, simple, trace); - oct_tx_enq1 (vm, ctx, b[5], l + 5, f5, simple, trace); + oct_tx_enq1 (vm, ctx, b[4], l + n, f4, simple, trace, &n, &dpl[n]); + oct_tx_enq1 (vm, ctx, b[5], l + n, f5, simple, trace, &n, &dpl[n]); vlib_prefetch_buffer_header (b[15], LOAD); - oct_tx_enq1 (vm, ctx, b[6], l + 6, f6, simple, trace); - oct_tx_enq1 (vm, ctx, b[7], l + 7, f7, simple, trace); - dpl[0] = dpl[1] = dpl[2] = dpl[3] = 2; - dpl[4] = dpl[5] = dpl[6] = dpl[7] = 2; + oct_tx_enq1 (vm, ctx, b[6], l + n, f6, simple, trace, &n, &dpl[n]); + oct_tx_enq1 (vm, ctx, b[7], l + n, f7, simple, trace, &n, &dpl[n]); } else { int simple = 0; - dpl[0] = oct_tx_enq1 (vm, ctx, b[0], l, f0, simple, trace); - dpl[1] = oct_tx_enq1 (vm, ctx, b[1], l + 1, f1, simple, trace); + oct_tx_enq1 (vm, ctx, b[0], l, f0, simple, trace, &n, &dpl[n]); + oct_tx_enq1 (vm, ctx, b[1], l + n, f1, simple, trace, &n, &dpl[n]); vlib_prefetch_buffer_header (b[13], LOAD); - dpl[2] = oct_tx_enq1 (vm, ctx, b[2], l + 2, f2, simple, trace); - dpl[3] = oct_tx_enq1 (vm, ctx, b[3], l + 3, f3, simple, trace); + oct_tx_enq1 (vm, ctx, b[2], l + n, f2, simple, trace, &n, &dpl[n]); + oct_tx_enq1 (vm, ctx, b[3], l + n, f3, simple, trace, &n, &dpl[n]); vlib_prefetch_buffer_header (b[14], LOAD); - dpl[4] = oct_tx_enq1 (vm, ctx, b[4], l + 4, f4, simple, trace); - dpl[5] = oct_tx_enq1 (vm, ctx, b[5], l + 5, f5, simple, trace); + oct_tx_enq1 (vm, ctx, b[4], l + n, f4, simple, trace, &n, &dpl[n]); + oct_tx_enq1 (vm, ctx, b[5], l + n, f5, simple, trace, &n, &dpl[n]); vlib_prefetch_buffer_header (b[15], LOAD); - dpl[6] = oct_tx_enq1 (vm, ctx, b[6], l + 6, f6, simple, trace); - dpl[7] = oct_tx_enq1 (vm, ctx, b[7], l + 7, f7, simple, trace); + oct_tx_enq1 (vm, ctx, b[6], l + n, f6, simple, trace, &n, &dpl[n]); + oct_tx_enq1 (vm, ctx, b[7], l + n, f7, simple, trace, &n, &dpl[n]); } - dpl += 8; + dpl += n; + l += n; + n = 0; } - for (; n_left > 0; n_left -= 1, b += 1, l += 1) + for (; n_left > 0; n_left -= 1, b += 1) { u32 f0 = b[0]->flags; - dpl++[0] = oct_tx_enq1 (vm, ctx, b[0], l, f0, 0, trace); + oct_tx_enq1 (vm, ctx, b[0], l, f0, 0, trace, &n, &dpl[n]); or_flags_16 |= f0; + dpl += n; + l += n; + n = 0; } lmt_arg = ctx->lmt_id; ioaddr = ctx->lmt_ioaddr; - n_lines = n_pkts; + n_lines = dpl - dwords_per_line; + + if (PREDICT_FALSE (!n_lines)) + return n_pkts; +#ifndef PLATFORM_OCTEON9 if (PREDICT_FALSE (or_flags_16 & VLIB_BUFFER_NEXT_PRESENT)) { dpl = dwords_per_line; @@ -340,6 +408,7 @@ oct_tx_enq16 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq, } roc_lmt_submit_steorl (lmt_arg, ioaddr); +#endif return n_pkts; } @@ -350,11 +419,17 @@ VNET_DEV_NODE_FN (oct_tx_node) vnet_dev_tx_node_runtime_t *rt = vnet_dev_get_tx_node_runtime (node); vnet_dev_tx_queue_t *txq = rt->tx_queue; oct_txq_t *ctq = vnet_dev_get_tx_queue_data (txq); + vnet_dev_t *dev = txq->port->dev; + oct_device_t *cd = vnet_dev_get_data (dev); u32 node_index = node->node_index; u32 *from = vlib_frame_vector_args (frame); u32 n, n_enq, n_left, n_pkts = frame->n_vectors; vlib_buffer_t *buffers[VLIB_FRAME_SIZE + 8], **b = buffers; +#ifdef PLATFORM_OCTEON9 + u64 lmt_id = 0; +#else u64 lmt_id = vm->thread_index << ROC_LMT_LINES_PER_CORE_LOG2; +#endif oct_tx_ctx_t ctx = { .node = node, @@ -363,6 +438,7 @@ VNET_DEV_NODE_FN (oct_tx_node) .sq = ctq->sq.qid, .sizem1 = 1, }, + .max_pkt_len = roc_nix_max_pkt_len (cd->nix), .lmt_id = lmt_id, .lmt_ioaddr = ctq->io_addr, .lmt_lines = ctq->lmt_addr + (lmt_id << ROC_LMT_LINE_SIZE_LOG2), @@ -396,7 +472,7 @@ VNET_DEV_NODE_FN (oct_tx_node) n += oct_tx_enq16 (vm, &ctx, txq, b, n_left, /* trace */ 0); } - ctq->n_enq = n_enq + n; + ctq->n_enq = n_enq + n - ctx.n_drop - ctx.n_exd_mtu; if (n < n_pkts) { @@ -411,6 +487,10 @@ VNET_DEV_NODE_FN (oct_tx_node) vlib_error_count (vm, node->node_index, OCT_TX_NODE_CTR_CHAIN_TOO_LONG, ctx.n_drop); + if (PREDICT_FALSE (ctx.n_exd_mtu)) + vlib_error_count (vm, node->node_index, OCT_TX_NODE_CTR_MTU_EXCEEDED, + ctx.n_exd_mtu); + if (ctx.batch_alloc_not_ready) vlib_error_count (vm, node_index, OCT_TX_NODE_CTR_AURA_BATCH_ALLOC_NOT_READY, @@ -431,5 +511,13 @@ VNET_DEV_NODE_FN (oct_tx_node) n_pkts -= ctx.n_drop; } + if (PREDICT_FALSE (ctx.n_exd_mtu)) + { + u32 bi[VLIB_FRAME_SIZE]; + vlib_get_buffer_indices (vm, ctx.exd_mtu, bi, ctx.n_exd_mtu); + vlib_buffer_free (vm, bi, ctx.n_exd_mtu); + n_pkts -= ctx.n_exd_mtu; + } + return n_pkts; } diff --git a/src/plugins/dpdk/device/cli.c b/src/plugins/dpdk/device/cli.c index c838800deb4..77f9a27f97b 100644 --- a/src/plugins/dpdk/device/cli.c +++ b/src/plugins/dpdk/device/cli.c @@ -89,12 +89,18 @@ show_dpdk_physmem (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { clib_error_t *err = 0; - u32 pipe_max_size; int fds[2]; u8 *s = 0; int n, n_try; FILE *f; + /* + * XXX: Pipes on FreeBSD grow dynamically up to 64KB (FreeBSD 15), don't + * manually tweak this value on FreeBSD at the moment. + */ +#ifdef __linux__ + u32 pipe_max_size; + err = clib_sysfs_read ("/proc/sys/fs/pipe-max-size", "%u", &pipe_max_size); if (err) @@ -112,6 +118,7 @@ show_dpdk_physmem (vlib_main_t * vm, unformat_input_t * input, err = clib_error_return_unix (0, "fcntl(F_SETPIPE_SZ)"); goto error; } +#endif /* __linux__ */ if (fcntl (fds[0], F_SETFL, O_NONBLOCK) == -1) { diff --git a/src/plugins/dpdk/device/init.c b/src/plugins/dpdk/device/init.c index 2d038b907bf..e416efe2e4d 100644 --- a/src/plugins/dpdk/device/init.c +++ b/src/plugins/dpdk/device/init.c @@ -1045,12 +1045,14 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) dpdk_main_t *dm = &dpdk_main; clib_error_t *error = 0; dpdk_config_main_t *conf = &dpdk_config_main; - vlib_thread_main_t *tm = vlib_get_thread_main (); dpdk_device_config_t *devconf; vlib_pci_addr_t pci_addr = { 0 }; vlib_vmbus_addr_t vmbus_addr = { 0 }; unformat_input_t sub_input; +#ifdef __linux + vlib_thread_main_t *tm = vlib_get_thread_main (); uword default_hugepage_sz, x; +#endif /* __linux__ */ u8 *s, *tmp = 0; int ret, i; int num_whitelisted = 0; @@ -1258,6 +1260,11 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) { vec_add1 (conf->eal_init_args, (u8 *) "--in-memory"); +#ifdef __linux__ + /* + * FreeBSD performs huge page prealloc through a dedicated kernel mode + * this process is only required on Linux. + */ default_hugepage_sz = clib_mem_get_default_hugepage_size (); clib_bitmap_foreach (x, tm->cpu_socket_bitmap) @@ -1272,6 +1279,7 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) if ((e = clib_sysfs_prealloc_hugepages(x, 0, n_pages))) clib_error_report (e); } +#endif /* __linux__ */ } /* on/off dpdk's telemetry thread */ @@ -1280,6 +1288,7 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) vec_add1 (conf->eal_init_args, (u8 *) "--no-telemetry"); } +#ifdef __linux__ if (!file_prefix) { tmp = format (0, "--file-prefix%c", 0); @@ -1287,6 +1296,7 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) tmp = format (0, "vpp%c", 0); vec_add1 (conf->eal_init_args, tmp); } +#endif if (no_pci == 0 && geteuid () == 0) dpdk_bind_devices_to_uio (conf); diff --git a/src/plugins/hs_apps/http_cli.c b/src/plugins/hs_apps/http_cli.c index 5d4d49c0fba..4970da72012 100644 --- a/src/plugins/hs_apps/http_cli.c +++ b/src/plugins/hs_apps/http_cli.c @@ -18,11 +18,20 @@ #include <vnet/session/session.h> #include <http/http.h> +#define HCS_DEBUG 0 + +#if HCS_DEBUG +#define HCS_DBG(_fmt, _args...) clib_warning (_fmt, ##_args) +#else +#define HCS_DBG(_fmt, _args...) +#endif + typedef struct { u32 hs_index; u32 thread_index; u64 node_index; + u8 plain_text; u8 *buf; } hcs_cli_args_t; @@ -139,7 +148,8 @@ hcs_cli_output (uword arg, u8 *buffer, uword buffer_bytes) } static void -start_send_data (hcs_session_t *hs, http_status_code_t status) +start_send_data (hcs_session_t *hs, http_status_code_t status, + http_content_type_t type) { http_msg_t msg; session_t *ts; @@ -147,7 +157,7 @@ start_send_data (hcs_session_t *hs, http_status_code_t status) msg.type = HTTP_MSG_REPLY; msg.code = status; - msg.content_type = HTTP_CONTENT_TEXT_HTML; + msg.content_type = type; msg.data.type = HTTP_MSG_DATA_INLINE; msg.data.len = vec_len (hs->tx_buf); @@ -181,6 +191,7 @@ send_data_to_http (void *rpc_args) { hcs_cli_args_t *args = (hcs_cli_args_t *) rpc_args; hcs_session_t *hs; + http_content_type_t type = HTTP_CONTENT_TEXT_HTML; hs = hcs_session_get (args->thread_index, args->hs_index); if (!hs) @@ -190,7 +201,9 @@ send_data_to_http (void *rpc_args) } hs->tx_buf = args->buf; - start_send_data (hs, HTTP_STATUS_OK); + if (args->plain_text) + type = HTTP_CONTENT_TEXT_PLAIN; + start_send_data (hs, HTTP_STATUS_OK, type); cleanup: @@ -218,17 +231,9 @@ hcs_cli_process (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f) { if (request[i] == '/') request[i] = ' '; - else if (request[i] == ' ') - { - /* vlib_cli_input is vector-based, no need for a NULL */ - vec_set_len (request, i); - break; - } i++; } - - /* Generate the html header */ - html = format (0, html_header_template, request /* title */ ); + HCS_DBG ("%v", request); /* Run the command */ unformat_init_vector (&input, vec_dup (request)); @@ -236,9 +241,17 @@ hcs_cli_process (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f) unformat_free (&input); request = 0; - /* Generate the html page */ - html = format (html, "%v", reply); - html = format (html, html_footer); + if (args->plain_text) + { + html = format (0, "%v", reply); + } + else + { + /* Generate the html page */ + html = format (0, html_header_template, request /* title */); + html = format (html, "%v", reply); + html = format (html, html_footer); + } /* Send it */ rpc_args = clib_mem_alloc (sizeof (*args)); @@ -308,9 +321,10 @@ hcs_ts_rx_callback (session_t *ts) hcs_cli_args_t args = {}; hcs_session_t *hs; http_msg_t msg; - int rv; + int rv, is_encoded = 0; hs = hcs_session_get (ts->thread_index, ts->opaque); + hs->tx_buf = 0; /* Read the http message header */ rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg); @@ -318,16 +332,65 @@ hcs_ts_rx_callback (session_t *ts) if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_GET) { + start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED, + HTTP_CONTENT_TEXT_HTML); + goto done; + } + + if (msg.data.target_path_len == 0 || + msg.data.target_form != HTTP_TARGET_ORIGIN_FORM) + { hs->tx_buf = 0; - start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED); - return 0; + start_send_data (hs, HTTP_STATUS_BAD_REQUEST, HTTP_CONTENT_TEXT_HTML); + goto done; } /* send the command to a new/recycled vlib process */ - vec_validate (args.buf, msg.data.len - 1); - rv = svm_fifo_dequeue (ts->rx_fifo, msg.data.len, args.buf); - ASSERT (rv == msg.data.len); - vec_set_len (args.buf, rv); + vec_validate (args.buf, msg.data.target_path_len - 1); + rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset, + msg.data.target_path_len, args.buf); + ASSERT (rv == msg.data.target_path_len); + HCS_DBG ("%v", args.buf); + if (http_validate_abs_path_syntax (args.buf, &is_encoded)) + { + start_send_data (hs, HTTP_STATUS_BAD_REQUEST, HTTP_CONTENT_TEXT_HTML); + vec_free (args.buf); + goto done; + } + if (is_encoded) + { + u8 *decoded = http_percent_decode (args.buf); + vec_free (args.buf); + args.buf = decoded; + } + + if (msg.data.headers_len) + { + u8 *headers = 0; + http_header_table_t *ht; + vec_validate (headers, msg.data.headers_len - 1); + rv = svm_fifo_peek (ts->rx_fifo, msg.data.headers_offset, + msg.data.headers_len, headers); + ASSERT (rv == msg.data.headers_len); + if (http_parse_headers (headers, &ht)) + { + start_send_data (hs, HTTP_STATUS_BAD_REQUEST, + HTTP_CONTENT_TEXT_HTML); + vec_free (args.buf); + vec_free (headers); + goto done; + } + const char *accept_value = http_get_header (ht, HTTP_HEADER_ACCEPT); + if (accept_value) + { + HCS_DBG ("client accept: %s", accept_value); + /* just for testing purpose, we don't care about precedence */ + if (strstr (accept_value, "text/plain")) + args.plain_text = 1; + } + http_free_header_table (ht); + vec_free (headers); + } args.hs_index = hs->session_index; args.thread_index = ts->thread_index; @@ -338,6 +401,9 @@ hcs_ts_rx_callback (session_t *ts) sizeof (args)); else alloc_cli_process (&args); + +done: + svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.len); return 0; } diff --git a/src/plugins/hs_apps/http_client_cli.c b/src/plugins/hs_apps/http_client_cli.c index 085a2b69bf7..a99169bafea 100644 --- a/src/plugins/hs_apps/http_client_cli.c +++ b/src/plugins/hs_apps/http_client_cli.c @@ -13,11 +13,9 @@ * limitations under the License. */ -#include <vnet/session/application.h> #include <vnet/session/application_interface.h> #include <vnet/session/session.h> #include <http/http.h> -#include <hs_apps/http_cli.h> #define HCC_DEBUG 0 @@ -68,6 +66,8 @@ typedef struct typedef enum { HCC_REPLY_RECEIVED = 100, + HCC_TRANSPORT_CLOSED, + HCC_CONNECT_FAILED, } hcc_cli_signal_t; static hcc_main_t hcc_main; @@ -136,6 +136,8 @@ hcc_ts_connected_callback (u32 app_index, u32 hc_index, session_t *as, { clib_warning ("connected error: hc_index(%d): %U", hc_index, format_session_error, err); + vlib_process_signal_event_mt (hcm->vlib_main, hcm->cli_node_index, + HCC_CONNECT_FAILED, 0); return -1; } @@ -273,6 +275,17 @@ hcc_ts_cleanup_callback (session_t *s, session_cleanup_ntf_t ntf) hcc_session_free (s->thread_index, hs); } +static void +hcc_ts_transport_closed (session_t *s) +{ + hcc_main_t *hcm = &hcc_main; + + HCC_DBG ("transport closed"); + + vlib_process_signal_event_mt (hcm->vlib_main, hcm->cli_node_index, + HCC_TRANSPORT_CLOSED, 0); +} + static session_cb_vft_t hcc_session_cb_vft = { .session_accept_callback = hcc_ts_accept_callback, .session_disconnect_callback = hcc_ts_disconnect_callback, @@ -281,6 +294,7 @@ static session_cb_vft_t hcc_session_cb_vft = { .builtin_app_tx_callback = hcc_ts_tx_callback, .session_reset_callback = hcc_ts_reset_callback, .session_cleanup_callback = hcc_ts_cleanup_callback, + .session_transport_closed_callback = hcc_ts_transport_closed, }; static clib_error_t * @@ -411,6 +425,12 @@ hcc_run (vlib_main_t *vm, int print_output) vlib_cli_output (vm, "%v", hcm->http_response); vec_free (hcm->http_response); break; + case HCC_TRANSPORT_CLOSED: + err = clib_error_return (0, "error, transport closed"); + break; + case HCC_CONNECT_FAILED: + err = clib_error_return (0, "failed to connect"); + break; default: err = clib_error_return (0, "unexpected event %d", event_type); break; diff --git a/src/plugins/hs_apps/http_tps.c b/src/plugins/hs_apps/http_tps.c index 920f7ea731f..3a086501f86 100644 --- a/src/plugins/hs_apps/http_tps.c +++ b/src/plugins/hs_apps/http_tps.c @@ -246,7 +246,7 @@ hts_start_send_data (hts_session_t *hs, http_status_code_t status) } static int -try_test_file (hts_session_t *hs, u8 *request) +try_test_file (hts_session_t *hs, u8 *target) { char *test_str = "test_file"; hts_main_t *htm = &hts_main; @@ -254,10 +254,10 @@ try_test_file (hts_session_t *hs, u8 *request) uword file_size; int rc = 0; - if (memcmp (request, test_str, clib_strnlen (test_str, 9))) + if (memcmp (target, test_str, clib_strnlen (test_str, 9))) return -1; - unformat_init_vector (&input, vec_dup (request)); + unformat_init_vector (&input, vec_dup (target)); if (!unformat (&input, "test_file_%U", unformat_memory_size, &file_size)) { rc = -1; @@ -297,8 +297,9 @@ done: static int hts_ts_rx_callback (session_t *ts) { + hts_main_t *htm = &hts_main; hts_session_t *hs; - u8 *request = 0; + u8 *target = 0; http_msg_t msg; int rv; @@ -314,20 +315,28 @@ hts_ts_rx_callback (session_t *ts) goto done; } - if (!msg.data.len) + if (msg.data.target_path_len == 0 || + msg.data.target_form != HTTP_TARGET_ORIGIN_FORM) { hts_start_send_data (hs, HTTP_STATUS_BAD_REQUEST); goto done; } - vec_validate (request, msg.data.len - 1); - rv = svm_fifo_dequeue (ts->rx_fifo, msg.data.len, request); + vec_validate (target, msg.data.target_path_len - 1); + rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset, + msg.data.target_path_len, target); + ASSERT (rv == msg.data.target_path_len); + + if (htm->debug_level) + clib_warning ("Request target: %v", target); - if (try_test_file (hs, request)) + if (try_test_file (hs, target)) hts_start_send_data (hs, HTTP_STATUS_NOT_FOUND); -done: + vec_free (target); +done: + svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.len); return 0; } diff --git a/src/plugins/http/http.c b/src/plugins/http/http.c index 0fa113c8155..72b4812fd42 100644 --- a/src/plugins/http/http.c +++ b/src/plugins/http/http.c @@ -83,6 +83,16 @@ format_http_state (u8 *s, va_list *va) } \ while (0) +static inline int +http_state_is_tx_valid (http_conn_t *hc) +{ + http_state_t state = hc->http_state; + return (state == HTTP_STATE_APP_IO_MORE_DATA || + state == HTTP_STATE_CLIENT_IO_MORE_DATA || + state == HTTP_STATE_WAIT_APP_REPLY || + state == HTTP_STATE_WAIT_APP_METHOD); +} + static inline http_worker_t * http_worker_get (u32 thread_index) { @@ -140,6 +150,7 @@ http_listener_free (http_conn_t *lhc) { http_main_t *hm = &http_main; + vec_free (lhc->app_name); if (CLIB_DEBUG) memset (lhc, 0xfc, sizeof (*lhc)); pool_put (hm->listener_pool, lhc); @@ -266,17 +277,21 @@ http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts, app_worker_t *app_wrk; int rv; + ho_hc = http_conn_get_w_thread (ho_hc_index, 0); + ASSERT (ho_hc->state == HTTP_CONN_STATE_CONNECTING); + if (err) { - clib_warning ("ERROR: %d", err); + clib_warning ("half-open hc index %d, error: %U", ho_hc_index, + format_session_error, err); + app_wrk = app_worker_get_if_valid (ho_hc->h_pa_wrk_index); + if (app_wrk) + app_worker_connect_notify (app_wrk, 0, err, ho_hc->h_pa_app_api_ctx); return 0; } new_hc_index = http_conn_alloc_w_thread (ts->thread_index); hc = http_conn_get_w_thread (new_hc_index, ts->thread_index); - ho_hc = http_conn_get_w_thread (ho_hc_index, 0); - - ASSERT (ho_hc->state == HTTP_CONN_STATE_CONNECTING); clib_memcpy_fast (hc, ho_hc, sizeof (*hc)); @@ -372,13 +387,13 @@ static const char *http_redirect_template = "HTTP/1.1 %s\r\n"; static const char *http_response_template = "HTTP/1.1 %s\r\n" "Date: %U GMT\r\n" "Expires: %U GMT\r\n" - "Server: VPP Static\r\n" + "Server: %s\r\n" "Content-Type: %s\r\n" "Content-Length: %lu\r\n\r\n"; static const char *http_request_template = "GET %s HTTP/1.1\r\n" - "User-Agent: VPP HTTP client\r\n" - "Accept: */*\r\n"; + "User-Agent: %s\r\n" + "Accept: */*\r\n\r\n"; static u32 http_send_data (http_conn_t *hc, u8 *data, u32 length, u32 offset) @@ -444,8 +459,18 @@ http_read_message (http_conn_t *hc) return 0; } -static int -v_find_index (u8 *vec, u32 offset, char *str) +/** + * @brief Find the first occurrence of the string in the vector. + * + * @param vec The vector to be scanned. + * @param offset Search offset in the vector. + * @param num Maximum number of characters to be searched if non-zero. + * @param str The string to be searched. + * + * @return @c -1 if the string is not found within the vector; index otherwise. + */ +static inline int +v_find_index (u8 *vec, u32 offset, u32 num, char *str) { int start_index = offset; u32 slen = (u32) strnlen_s_inline (str, 16); @@ -456,7 +481,15 @@ v_find_index (u8 *vec, u32 offset, char *str) if (vlen <= slen) return -1; - for (; start_index < (vlen - slen); start_index++) + int end_index = vlen - slen; + if (num) + { + if (num < slen) + return -1; + end_index = clib_min (end_index, offset + num - slen); + } + + for (; start_index <= end_index; start_index++) { if (!memcmp (vec + start_index, str, slen)) return start_index; @@ -465,6 +498,259 @@ v_find_index (u8 *vec, u32 offset, char *str) return -1; } +static void +http_identify_optional_query (http_conn_t *hc) +{ + u32 pos = vec_search (hc->rx_buf, '?'); + if (~0 != pos) + { + hc->target_query_offset = pos + 1; + hc->target_query_len = + hc->target_path_offset + hc->target_path_len - hc->target_query_offset; + hc->target_path_len = hc->target_path_len - hc->target_query_len - 1; + } +} + +static int +http_get_target_form (http_conn_t *hc) +{ + int i; + + /* "*" */ + if ((hc->rx_buf[hc->target_path_offset] == '*') && + (hc->target_path_len == 1)) + { + hc->target_form = HTTP_TARGET_ASTERISK_FORM; + return 0; + } + + /* 1*( "/" segment ) [ "?" query ] */ + if (hc->rx_buf[hc->target_path_offset] == '/') + { + /* drop leading slash */ + hc->target_path_len--; + hc->target_path_offset++; + hc->target_form = HTTP_TARGET_ORIGIN_FORM; + http_identify_optional_query (hc); + return 0; + } + + /* scheme "://" host [ ":" port ] *( "/" segment ) [ "?" query ] */ + i = v_find_index (hc->rx_buf, hc->target_path_offset, hc->target_path_len, + "://"); + if (i > 0) + { + hc->target_form = HTTP_TARGET_ABSOLUTE_FORM; + http_identify_optional_query (hc); + return 0; + } + + /* host ":" port */ + for (i = hc->target_path_offset; + i < (hc->target_path_offset + hc->target_path_len); i++) + { + if ((hc->rx_buf[i] == ':') && (isdigit (hc->rx_buf[i + 1]))) + { + hc->target_form = HTTP_TARGET_AUTHORITY_FORM; + return 0; + } + } + + return -1; +} + +static int +http_parse_request_line (http_conn_t *hc, http_status_code_t *ec) +{ + int i, target_len; + u32 next_line_offset; + + /* request-line = method SP request-target SP HTTP-version CRLF */ + i = v_find_index (hc->rx_buf, 0, 0, "\r\n"); + if (i < 0) + { + clib_warning ("request line incomplete"); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } + HTTP_DBG (0, "request line length: %d", i); + next_line_offset = i + 2; + + /* there should be at least one more CRLF */ + if (vec_len (hc->rx_buf) < (next_line_offset + 2)) + { + clib_warning ("malformed message, too short"); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } + + /* parse method */ + if ((i = v_find_index (hc->rx_buf, 0, next_line_offset, "GET ")) >= 0) + { + HTTP_DBG (0, "GET method"); + hc->method = HTTP_REQ_GET; + hc->target_path_offset = i + 4; + } + else if ((i = v_find_index (hc->rx_buf, 0, next_line_offset, "POST ")) >= 0) + { + HTTP_DBG (0, "POST method"); + hc->method = HTTP_REQ_POST; + hc->target_path_offset = i + 5; + } + else + { + clib_warning ("method not implemented: %8v", hc->rx_buf); + *ec = HTTP_STATUS_NOT_IMPLEMENTED; + return -1; + } + + /* find version */ + i = v_find_index (hc->rx_buf, next_line_offset - 11, 11, " HTTP/"); + if (i < 0) + { + clib_warning ("HTTP version not present"); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } + /* verify major version */ + if (isdigit (hc->rx_buf[i + 6])) + { + if (hc->rx_buf[i + 6] != '1') + { + clib_warning ("HTTP major version '%c' not supported", + hc->rx_buf[i + 6]); + *ec = HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED; + return -1; + } + } + else + { + clib_warning ("HTTP major version '%c' is not digit", hc->rx_buf[i + 6]); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } + + /* parse request-target */ + target_len = i - hc->target_path_offset; + if (target_len < 1) + { + clib_warning ("request-target not present"); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } + hc->target_path_len = target_len; + hc->target_query_offset = 0; + hc->target_query_len = 0; + if (http_get_target_form (hc)) + { + clib_warning ("invalid target"); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } + HTTP_DBG (0, "request-target path length: %u", hc->target_path_len); + HTTP_DBG (0, "request-target path offset: %u", hc->target_path_offset); + HTTP_DBG (0, "request-target query length: %u", hc->target_query_len); + HTTP_DBG (0, "request-target query offset: %u", hc->target_query_offset); + + /* set buffer offset to nex line start */ + hc->rx_buf_offset = next_line_offset; + + return 0; +} + +static int +http_identify_headers (http_conn_t *hc, http_status_code_t *ec) +{ + int i; + + /* check if we have any header */ + if ((hc->rx_buf[hc->rx_buf_offset] == '\r') && + (hc->rx_buf[hc->rx_buf_offset + 1] == '\n')) + { + /* just another CRLF -> no headers */ + HTTP_DBG (0, "no headers"); + hc->headers_len = 0; + return 0; + } + + /* find empty line indicating end of header section */ + i = v_find_index (hc->rx_buf, hc->rx_buf_offset, 0, "\r\n\r\n"); + if (i < 0) + { + clib_warning ("cannot find header section end"); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } + hc->headers_offset = hc->rx_buf_offset; + hc->headers_len = i - hc->rx_buf_offset + 2; + HTTP_DBG (0, "headers length: %u", hc->headers_len); + HTTP_DBG (0, "headers offset: %u", hc->headers_offset); + + return 0; +} + +static int +http_identify_message_body (http_conn_t *hc, http_status_code_t *ec) +{ + unformat_input_t input; + int i, len; + u8 *line; + + hc->body_len = 0; + + if (hc->headers_len == 0) + { + HTTP_DBG (0, "no header, no message-body"); + return 0; + } + + /* TODO check for chunked transfer coding */ + + /* try to find Content-Length header */ + i = v_find_index (hc->rx_buf, hc->headers_offset, hc->headers_len, + "Content-Length:"); + if (i < 0) + { + HTTP_DBG (0, "Content-Length header not present, no message-body"); + return 0; + } + hc->rx_buf_offset = i + 15; + + i = v_find_index (hc->rx_buf, hc->rx_buf_offset, hc->headers_len, "\r\n"); + if (i < 0) + { + clib_warning ("end of line missing"); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } + len = i - hc->rx_buf_offset; + if (len < 1) + { + clib_warning ("invalid header, content length value missing"); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } + + line = vec_new (u8, len); + clib_memcpy (line, hc->rx_buf + hc->rx_buf_offset, len); + HTTP_DBG (0, "%v", line); + + unformat_init_vector (&input, line); + if (!unformat (&input, "%lu", &hc->body_len)) + { + clib_warning ("failed to unformat content length value"); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } + unformat_free (&input); + + hc->body_offset = hc->headers_offset + hc->headers_len + 2; + HTTP_DBG (0, "body length: %u", hc->body_len); + HTTP_DBG (0, "body offset: %u", hc->body_offset); + + return 0; +} + static int http_parse_header (http_conn_t *hc, int *content_length) { @@ -472,7 +758,7 @@ http_parse_header (http_conn_t *hc, int *content_length) int i, len; u8 *line; - i = v_find_index (hc->rx_buf, hc->rx_buf_offset, CONTENT_LEN_STR); + i = v_find_index (hc->rx_buf, hc->rx_buf_offset, 0, CONTENT_LEN_STR); if (i < 0) { clib_warning ("cannot find '%s' in the header!", CONTENT_LEN_STR); @@ -481,7 +767,7 @@ http_parse_header (http_conn_t *hc, int *content_length) hc->rx_buf_offset = i; - i = v_find_index (hc->rx_buf, hc->rx_buf_offset, "\n"); + i = v_find_index (hc->rx_buf, hc->rx_buf_offset, 0, "\n"); if (i < 0) { clib_warning ("end of line missing; incomplete data"); @@ -502,7 +788,7 @@ http_parse_header (http_conn_t *hc, int *content_length) /* skip rest of the header */ hc->rx_buf_offset += len; - i = v_find_index (hc->rx_buf, hc->rx_buf_offset, "<html>"); + i = v_find_index (hc->rx_buf, hc->rx_buf_offset, 0, "<html>"); if (i < 0) { clib_warning ("<html> tag not found"); @@ -520,21 +806,23 @@ http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp) http_msg_t msg = {}; app_worker_t *app_wrk; session_t *as; - http_status_code_t ec; rv = http_read_message (hc); /* Nothing yet, wait for data or timer expire */ if (rv) - return HTTP_SM_STOP; + { + HTTP_DBG (1, "no data to deq"); + return HTTP_SM_STOP; + } if (vec_len (hc->rx_buf) < 8) { - ec = HTTP_STATUS_BAD_REQUEST; + clib_warning ("response buffer too short"); goto error; } - if ((i = v_find_index (hc->rx_buf, 0, "200 OK")) >= 0) + if ((i = v_find_index (hc->rx_buf, 0, 0, "200 OK")) >= 0) { msg.type = HTTP_MSG_REPLY; msg.content_type = HTTP_CONTENT_TEXT_HTML; @@ -546,9 +834,7 @@ http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp) if (rv) { clib_warning ("failed to parse http reply"); - session_transport_closing_notify (&hc->connection); - http_disconnect_transport (hc); - return -1; + goto error; } msg.data.len = content_length; u32 dlen = vec_len (hc->rx_buf) - hc->rx_buf_offset; @@ -591,17 +877,14 @@ http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp) } else { - HTTP_DBG (0, "Unknown http method %v", hc->rx_buf); - ec = HTTP_STATUS_METHOD_NOT_ALLOWED; + clib_warning ("Unknown http method %v", hc->rx_buf); goto error; } error: - - http_send_error (hc, ec); session_transport_closing_notify (&hc->connection); + session_transport_closed_notify (&hc->connection); http_disconnect_transport (hc); - return HTTP_SM_ERROR; } @@ -612,9 +895,8 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp) app_worker_t *app_wrk; http_msg_t msg; session_t *as; - int i, rv; + int rv; u32 len; - u8 *buf; rv = http_read_message (hc); @@ -622,50 +904,45 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp) if (rv) return HTTP_SM_STOP; + HTTP_DBG (0, "%v", hc->rx_buf); + if (vec_len (hc->rx_buf) < 8) { ec = HTTP_STATUS_BAD_REQUEST; goto error; } - if ((i = v_find_index (hc->rx_buf, 0, "GET ")) >= 0) - { - hc->method = HTTP_REQ_GET; - hc->rx_buf_offset = i + 5; + rv = http_parse_request_line (hc, &ec); + if (rv) + goto error; - i = v_find_index (hc->rx_buf, hc->rx_buf_offset, "HTTP"); - if (i < 0) - { - ec = HTTP_STATUS_BAD_REQUEST; - goto error; - } + rv = http_identify_headers (hc, &ec); + if (rv) + goto error; - HTTP_DBG (0, "GET method %v", hc->rx_buf); - len = i - hc->rx_buf_offset - 1; - } - else if ((i = v_find_index (hc->rx_buf, 0, "POST ")) >= 0) - { - hc->method = HTTP_REQ_POST; - hc->rx_buf_offset = i + 6; - len = vec_len (hc->rx_buf) - hc->rx_buf_offset - 1; - HTTP_DBG (0, "POST method %v", hc->rx_buf); - } - else - { - HTTP_DBG (0, "Unknown http method %v", hc->rx_buf); - ec = HTTP_STATUS_METHOD_NOT_ALLOWED; - goto error; - } + rv = http_identify_message_body (hc, &ec); + if (rv) + goto error; - buf = &hc->rx_buf[hc->rx_buf_offset]; + len = vec_len (hc->rx_buf); msg.type = HTTP_MSG_REQUEST; msg.method_type = hc->method; msg.content_type = HTTP_CONTENT_TEXT_HTML; msg.data.type = HTTP_MSG_DATA_INLINE; msg.data.len = len; - - svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) }, { buf, len } }; + msg.data.target_form = hc->target_form; + msg.data.target_path_offset = hc->target_path_offset; + msg.data.target_path_len = hc->target_path_len; + msg.data.target_query_offset = hc->target_query_offset; + msg.data.target_query_len = hc->target_query_len; + msg.data.headers_offset = hc->headers_offset; + msg.data.headers_len = hc->headers_len; + msg.data.body_offset = hc->body_offset; + msg.data.body_len = hc->body_len; + + svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) }, + { hc->rx_buf, len } }; as = session_get_from_handle (hc->h_pa_session_handle); rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2, 0 /* allow partial */); @@ -734,6 +1011,7 @@ http_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp) * Add headers. For now: * - current time * - expiration time + * - server name * - content type * - data length */ @@ -741,6 +1019,11 @@ http_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp) switch (msg.code) { + case HTTP_STATUS_NOT_FOUND: + case HTTP_STATUS_METHOD_NOT_ALLOWED: + case HTTP_STATUS_BAD_REQUEST: + case HTTP_STATUS_INTERNAL_ERROR: + case HTTP_STATUS_FORBIDDEN: case HTTP_STATUS_OK: header = format (0, http_response_template, http_status_code_str[msg.code], @@ -748,6 +1031,8 @@ http_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp) format_clib_timebase_time, now, /* Expires */ format_clib_timebase_time, now + 600.0, + /* Server */ + hc->app_name, /* Content type */ http_content_type_str[msg.content_type], /* Length */ @@ -759,6 +1044,7 @@ http_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp) /* Location: http(s)://new-place already queued up as data */ break; default: + clib_warning ("unsupported status code: %d", msg.code); return HTTP_SM_ERROR; } @@ -814,11 +1100,18 @@ http_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp) goto error; } + /* currently we support only GET method */ + if (msg.method_type != HTTP_REQ_GET) + { + clib_warning ("unsupported method %d", msg.method_type); + goto error; + } + vec_validate (buf, msg.data.len - 1); rv = svm_fifo_dequeue (as->tx_fifo, msg.data.len, buf); ASSERT (rv == msg.data.len); - request = format (0, http_request_template, buf); + request = format (0, http_request_template, buf, hc->app_name); offset = http_send_data (hc, request, vec_len (request), 0); if (offset != vec_len (request)) { @@ -834,7 +1127,9 @@ http_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp) return HTTP_SM_STOP; error: + svm_fifo_dequeue_drop_all (as->tx_fifo); session_transport_closing_notify (&hc->connection); + session_transport_closed_notify (&hc->connection); http_disconnect_transport (hc); return HTTP_SM_ERROR; } @@ -893,6 +1188,13 @@ http_state_client_io_more_data (http_conn_t *hc, transport_send_params_t *sp) hc->to_recv -= rv; HTTP_DBG (1, "drained %d from ts; remains %d", rv, hc->to_recv); + if (hc->to_recv == 0) + { + hc->rx_buf_offset = 0; + vec_reset_length (hc->rx_buf); + http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD); + } + app_wrk = app_worker_get_if_valid (as->app_wrk_index); if (app_wrk) app_worker_rx_notify (app_wrk, as); @@ -1147,6 +1449,11 @@ http_transport_connect (transport_endpoint_cfg_t *tep) hc->state = HTTP_CONN_STATE_CONNECTING; cargs->api_context = hc_index; + if (vec_len (app->name)) + hc->app_name = vec_dup (app->name); + else + hc->app_name = format (0, "VPP HTTP client"); + HTTP_DBG (1, "hc ho_index %x", hc_index); if ((error = vnet_connect (cargs))) @@ -1199,6 +1506,11 @@ http_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep) lhc->c_s_index = app_listener_index; lhc->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP; + if (vec_len (app->name)) + lhc->app_name = vec_dup (app->name); + else + lhc->app_name = format (0, "VPP server app"); + return lhc_index; } @@ -1239,7 +1551,11 @@ http_transport_close (u32 hc_index, u32 thread_index) http_disconnect_transport (hc); return; } - + else if (hc->state == HTTP_CONN_STATE_CLOSED) + { + HTTP_DBG (1, "nothing to do, already closed"); + return; + } as = session_get_from_handle (hc->h_pa_session_handle); /* Nothing more to send, confirm close */ diff --git a/src/plugins/http/http.h b/src/plugins/http/http.h index dbae5ac4611..e3ee93b6291 100644 --- a/src/plugins/http/http.h +++ b/src/plugins/http/http.h @@ -16,6 +16,8 @@ #ifndef SRC_PLUGINS_HTTP_HTTP_H_ #define SRC_PLUGINS_HTTP_HTTP_H_ +#include <ctype.h> + #include <vnet/plugin/plugin.h> #include <vpp/app/version.h> @@ -83,6 +85,14 @@ typedef enum http_msg_type_ HTTP_MSG_REPLY } http_msg_type_t; +typedef enum http_target_form_ +{ + HTTP_TARGET_ORIGIN_FORM, + HTTP_TARGET_ABSOLUTE_FORM, + HTTP_TARGET_AUTHORITY_FORM, + HTTP_TARGET_ASTERISK_FORM +} http_target_form_t; + #define foreach_http_content_type \ _ (APP_7Z, ".7z", "application / x - 7z - compressed") \ _ (APP_DOC, ".doc", "application / msword") \ @@ -172,12 +182,50 @@ typedef enum http_content_type_ } http_content_type_t; #define foreach_http_status_code \ + _ (100, CONTINUE, "100 Continue") \ + _ (101, SWITCHING_PROTOCOLS, "101 Switching Protocols") \ _ (200, OK, "200 OK") \ + _ (201, CREATED, "201 Created") \ + _ (202, ACCEPTED, "202 Accepted") \ + _ (203, NON_UTHORITATIVE_INFORMATION, "203 Non-Authoritative Information") \ + _ (204, NO_CONTENT, "204 No Content") \ + _ (205, RESET_CONTENT, "205 Reset Content") \ + _ (206, PARTIAL_CONTENT, "206 Partial Content") \ + _ (300, MULTIPLE_CHOICES, "300 Multiple Choices") \ _ (301, MOVED, "301 Moved Permanently") \ + _ (302, FOUND, "302 Found") \ + _ (303, SEE_OTHER, "303 See Other") \ + _ (304, NOT_MODIFIED, "304 Not Modified") \ + _ (305, USE_PROXY, "305 Use Proxy") \ + _ (307, TEMPORARY_REDIRECT, "307 Temporary Redirect") \ + _ (308, PERMANENT_REDIRECT, "308 Permanent Redirect") \ _ (400, BAD_REQUEST, "400 Bad Request") \ + _ (401, UNAUTHORIZED, "401 Unauthorized") \ + _ (402, PAYMENT_REQUIRED, "402 Payment Required") \ + _ (403, FORBIDDEN, "403 Forbidden") \ _ (404, NOT_FOUND, "404 Not Found") \ _ (405, METHOD_NOT_ALLOWED, "405 Method Not Allowed") \ - _ (500, INTERNAL_ERROR, "500 Internal Server Error") + _ (406, NOT_ACCEPTABLE, "406 Not Acceptable") \ + _ (407, PROXY_AUTHENTICATION_REQUIRED, "407 Proxy Authentication Required") \ + _ (408, REQUEST_TIMEOUT, "408 Request Timeout") \ + _ (409, CONFLICT, "409 Conflict") \ + _ (410, GONE, "410 Gone") \ + _ (411, LENGTH_REQUIRED, "411 Length Required") \ + _ (412, PRECONDITION_FAILED, "412 Precondition Failed") \ + _ (413, CONTENT_TOO_LARGE, "413 Content Too Large") \ + _ (414, URI_TOO_LONG, "414 URI Too Long") \ + _ (415, UNSUPPORTED_MEDIA_TYPE, "415 Unsupported Media Type") \ + _ (416, RANGE_NOT_SATISFIABLE, "416 Range Not Satisfiable") \ + _ (417, EXPECTATION_FAILED, "417 Expectation Failed") \ + _ (421, MISDIRECTED_REQUEST, "421 Misdirected Request") \ + _ (422, UNPROCESSABLE_CONTENT, "422 Unprocessable_Content") \ + _ (426, UPGRADE_REQUIRED, "426 Upgrade Required") \ + _ (500, INTERNAL_ERROR, "500 Internal Server Error") \ + _ (501, NOT_IMPLEMENTED, "501 Not Implemented") \ + _ (502, BAD_GATEWAY, "502 Bad Gateway") \ + _ (503, SERVICE_UNAVAILABLE, "503 Service Unavailable") \ + _ (504, GATEWAY_TIMEOUT, "504 Gateway Timeout") \ + _ (505, HTTP_VERSION_NOT_SUPPORTED, "505 HTTP Version Not Supported") typedef enum http_status_code_ { @@ -187,6 +235,51 @@ typedef enum http_status_code_ HTTP_N_STATUS } http_status_code_t; +#define HTTP_HEADER_ACCEPT "Accept" +#define HTTP_HEADER_ACCEPT_CHARSET "Accept-Charset" +#define HTTP_HEADER_ACCEPT_ENCODING "Accept-Encoding" +#define HTTP_HEADER_ACCEPT_LANGUAGE "Accept-Language" +#define HTTP_HEADER_ACCEPT_RANGES "Accept-Ranges" +#define HTTP_HEADER_ALLOW "Allow" +#define HTTP_HEADER_AUTHENTICATION_INFO "Authentication-Info" +#define HTTP_HEADER_AUTHORIZATION "Authorization" +#define HTTP_HEADER_CLOSE "Close" +#define HTTP_HEADER_CONNECTION "Connection" +#define HTTP_HEADER_CONTENT_ENCODING "Content-Encoding" +#define HTTP_HEADER_CONTENT_LANGUAGE "Content-Language" +#define HTTP_HEADER_CONTENT_LENGTH "Content-Length" +#define HTTP_HEADER_CONTENT_LOCATION "Content-Location" +#define HTTP_HEADER_CONTENT_RANGE "Content-Range" +#define HTTP_HEADER_CONTENT_TYPE "Content-Type" +#define HTTP_HEADER_DATE "Date" +#define HTTP_HEADER_ETAG "ETag" +#define HTTP_HEADER_EXPECT "Expect" +#define HTTP_HEADER_FROM "From" +#define HTTP_HEADER_HOST "Host" +#define HTTP_HEADER_IF_MATCH "If-Match" +#define HTTP_HEADER_IF_MODIFIED_SINCE "If-Modified-Since" +#define HTTP_HEADER_IF_NONE_MATCH "If-None-Match" +#define HTTP_HEADER_IF_RANGE "If-Range" +#define HTTP_HEADER_IF_UNMODIFIED_SINCE "If-Unmodified-Since" +#define HTTP_HEADER_LAST_MODIFIED "Last-Modified" +#define HTTP_HEADER_LOCATION "Location" +#define HTTP_HEADER_MAX_FORWARDS "Max-Forwards" +#define HTTP_HEADER_PROXY_AUTHENTICATE "Proxy-Authenticate" +#define HTTP_HEADER_PROXY_AUTHENTICATION_INFO "Proxy-Authentication-Info" +#define HTTP_HEADER_PROXY_AUTHORIZATION "Proxy-Authorization" +#define HTTP_HEADER_RANGE "Range" +#define HTTP_HEADER_REFERER "Referer" +#define HTTP_HEADER_RETRY_AFTER "Retry-After" +#define HTTP_HEADER_SERVER "Server" +#define HTTP_HEADER_TE "TE" +#define HTTP_HEADER_TRAILER "Trailer" +#define HTTP_HEADER_TRANSFER_ENCODING "Transfer-Encoding" +#define HTTP_HEADER_UPGRADE "Upgrade" +#define HTTP_HEADER_USER_AGENT "User-Agent" +#define HTTP_HEADER_VARY "Vary" +#define HTTP_HEADER_VIA "Via" +#define HTTP_HEADER_WWW_AUTHENTICATE "WWW-Authenticate" + typedef enum http_msg_data_type_ { HTTP_MSG_DATA_INLINE, @@ -197,6 +290,15 @@ typedef struct http_msg_data_ { http_msg_data_type_t type; u64 len; + http_target_form_t target_form; + u32 target_path_offset; + u32 target_path_len; + u32 target_query_offset; + u32 target_query_len; + u32 headers_offset; + u32 headers_len; + u32 body_offset; + u32 body_len; u8 data[0]; } http_msg_data_t; @@ -227,6 +329,7 @@ typedef struct http_tc_ http_conn_state_t state; u32 timer_handle; + u8 *app_name; /* * Current request @@ -238,6 +341,15 @@ typedef struct http_tc_ http_buffer_t tx_buf; u32 to_recv; u32 bytes_dequeued; + http_target_form_t target_form; + u32 target_path_offset; + u32 target_path_len; + u32 target_query_offset; + u32 target_query_len; + u32 headers_offset; + u32 headers_len; + u32 body_offset; + u32 body_len; } http_conn_t; typedef struct http_worker_ @@ -266,14 +378,416 @@ typedef struct http_main_ u32 fifo_size; } http_main_t; -static inline int -http_state_is_tx_valid (http_conn_t *hc) +always_inline int +_validate_target_syntax (u8 *target, int is_query, int *is_encoded) { - http_state_t state = hc->http_state; - return (state == HTTP_STATE_APP_IO_MORE_DATA || - state == HTTP_STATE_CLIENT_IO_MORE_DATA || - state == HTTP_STATE_WAIT_APP_REPLY || - state == HTTP_STATE_WAIT_APP_METHOD); + int i, encoded = 0; + + static uword valid_chars[4] = { + /* !$&'()*+,-./0123456789:;= */ + 0x2fffffd200000000, + /* @ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ */ + 0x47fffffe87ffffff, + 0x0000000000000000, + 0x0000000000000000, + }; + + for (i = 0; i < vec_len (target); i++) + { + if (clib_bitmap_get_no_check (valid_chars, target[i])) + continue; + /* target was already split after first question mark, + * for query it is valid character */ + if (is_query && target[i] == '?') + continue; + /* pct-encoded = "%" HEXDIG HEXDIG */ + if (target[i] == '%') + { + if ((i + 2) > vec_len (target)) + return -1; + if (!isxdigit (target[i + 1]) || !isxdigit (target[i + 2])) + return -1; + i += 2; + encoded = 1; + continue; + } + clib_warning ("invalid character %d", target[i]); + return -1; + } + if (is_encoded) + *is_encoded = encoded; + return 0; +} + +/** + * An "absolute-path" rule validation (RFC9110 section 4.1). + * + * @param path Target path to validate. + * @param is_encoded Return flag that indicates if percent-encoded (optional). + * + * @return @c 0 on success. + */ +always_inline int +http_validate_abs_path_syntax (u8 *path, int *is_encoded) +{ + return _validate_target_syntax (path, 0, is_encoded); +} + +/** + * A "query" rule validation (RFC3986 section 2.1). + * + * @param query Target query to validate. + * @param is_encoded Return flag that indicates if percent-encoded (optional). + * + * @return @c 0 on success. + */ +always_inline int +http_validate_query_syntax (u8 *query, int *is_encoded) +{ + return _validate_target_syntax (query, 1, is_encoded); +} + +#define htoi(x) (isdigit (x) ? (x - '0') : (tolower (x) - 'a' + 10)) + +/** + * Decode percent-encoded data. + * + * @param src Data to decode. + * + * @return New vector with decoded data. + * + * The caller is always responsible to free the returned vector. + */ +always_inline u8 * +http_percent_decode (u8 *src) +{ + int i; + u8 *decoded_uri = 0; + + for (i = 0; i < vec_len (src); i++) + { + if (src[i] == '%') + { + u8 c = (htoi (src[i + 1]) << 4) | htoi (src[i + 2]); + vec_add1 (decoded_uri, c); + i += 2; + } + else + vec_add1 (decoded_uri, src[i]); + } + return decoded_uri; +} + +/** + * Remove dot segments from path (RFC3986 section 5.2.4) + * + * @param path Path to sanitize. + * + * @return New vector with sanitized path. + * + * The caller is always responsible to free the returned vector. + */ +always_inline u8 * +http_path_remove_dot_segments (u8 *path) +{ + u32 *segments = 0, *segments_len = 0, segment_len; + u8 *new_path = 0; + int i, ii; + + if (!path) + return vec_new (u8, 0); + + segments = vec_new (u32, 1); + /* first segment */ + segments[0] = 0; + /* find all segments */ + for (i = 1; i < (vec_len (path) - 1); i++) + { + if (path[i] == '/') + vec_add1 (segments, i + 1); + } + /* dummy tail */ + vec_add1 (segments, vec_len (path)); + + /* scan all segments for "." and ".." */ + segments_len = vec_new (u32, vec_len (segments) - 1); + for (i = 0; i < vec_len (segments_len); i++) + { + segment_len = segments[i + 1] - segments[i]; + if (segment_len == 2 && path[segments[i]] == '.') + segment_len = 0; + else if (segment_len == 3 && path[segments[i]] == '.' && + path[segments[i] + 1] == '.') + { + segment_len = 0; + /* remove parent (if any) */ + for (ii = i - 1; ii >= 0; ii--) + { + if (segments_len[ii]) + { + segments_len[ii] = 0; + break; + } + } + } + segments_len[i] = segment_len; + } + + /* we might end with empty path, so return at least empty vector */ + new_path = vec_new (u8, 0); + /* append all valid segments */ + for (i = 0; i < vec_len (segments_len); i++) + { + if (segments_len[i]) + vec_add (new_path, path + segments[i], segments_len[i]); + } + vec_free (segments); + vec_free (segments_len); + return new_path; +} + +always_inline int +_parse_field_name (u8 **pos, u8 *end, u8 **field_name_start, + u32 *field_name_len) +{ + u32 name_len = 0; + u8 *p; + + static uword tchar[4] = { + /* !#$%'*+-.0123456789 */ + 0x03ff6cba00000000, + /* ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~ */ + 0x57ffffffc7fffffe, + 0x0000000000000000, + 0x0000000000000000, + }; + + p = *pos; + + *field_name_start = p; + while (p != end) + { + if (clib_bitmap_get_no_check (tchar, *p)) + { + name_len++; + p++; + } + else if (*p == ':') + { + if (name_len == 0) + { + clib_warning ("empty field name"); + return -1; + } + *field_name_len = name_len; + p++; + *pos = p; + return 0; + } + else + { + clib_warning ("invalid character %d", *p); + return -1; + } + } + clib_warning ("field name end not found"); + return -1; +} + +always_inline int +_parse_field_value (u8 **pos, u8 *end, u8 **field_value_start, + u32 *field_value_len) +{ + u32 value_len = 0; + u8 *p; + + p = *pos; + + /* skip leading whitespace */ + while (1) + { + if (p == end) + { + clib_warning ("field value not found"); + return -1; + } + else if (*p != ' ' && *p != '\t') + { + break; + } + p++; + } + + *field_value_start = p; + while (p != end) + { + if (*p == '\r') + { + if ((end - p) < 1) + { + clib_warning ("incorrect field line end"); + return -1; + } + p++; + if (*p == '\n') + { + if (value_len == 0) + { + clib_warning ("empty field value"); + return -1; + } + p++; + *pos = p; + /* skip trailing whitespace */ + p = *field_value_start + value_len - 1; + while (*p == ' ' || *p == '\t') + { + p--; + value_len--; + } + *field_value_len = value_len; + return 0; + } + clib_warning ("CR without LF"); + return -1; + } + if (*p < ' ' && *p != '\t') + { + clib_warning ("invalid character %d", *p); + return -1; + } + p++; + value_len++; + } + + clib_warning ("field value end not found"); + return -1; +} + +typedef struct +{ + u8 *name; + u8 *value; +} http_header_t; + +typedef struct +{ + http_header_t *headers; + uword *value_by_name; +} http_header_table_t; + +/** + * Free header table's memory. + * + * @param ht Header table to free. + */ +always_inline void +http_free_header_table (http_header_table_t *ht) +{ + http_header_t *header; + vec_foreach (header, ht->headers) + { + vec_free (header->name); + vec_free (header->value); + } + vec_free (ht->headers); + hash_free (ht->value_by_name); + clib_mem_free (ht); +} + +/** + * Parse headers in given vector. + * + * @param headers Vector to parse. + * @param [out] header_table Parsed headers in case of success. + * + * @return @c 0 on success. + * + * The caller is responsible to free the returned @c header_table + * using @c http_free_header_table . + */ +always_inline int +http_parse_headers (u8 *headers, http_header_table_t **header_table) +{ + u8 *pos, *end, *name_start, *value_start, *name; + u32 name_len, value_len; + int rv; + http_header_t *header; + http_header_table_t *ht; + uword *p; + + end = headers + vec_len (headers); + pos = headers; + + ht = clib_mem_alloc (sizeof (*ht)); + ht->value_by_name = hash_create_string (0, sizeof (uword)); + ht->headers = 0; + do + { + rv = _parse_field_name (&pos, end, &name_start, &name_len); + if (rv != 0) + { + http_free_header_table (ht); + return rv; + } + rv = _parse_field_value (&pos, end, &value_start, &value_len); + if (rv != 0) + { + http_free_header_table (ht); + return rv; + } + name = vec_new (u8, name_len); + clib_memcpy (name, name_start, name_len); + vec_terminate_c_string (name); + /* check if header is repeated */ + p = hash_get_mem (ht->value_by_name, name); + if (p) + { + /* if yes combine values */ + header = vec_elt_at_index (ht->headers, p[0]); + vec_pop (header->value); /* drop null byte */ + header->value = format (header->value, ", %U%c", format_ascii_bytes, + value_start, value_len, 0); + vec_free (name); + continue; + } + /* or create new record */ + vec_add2 (ht->headers, header, sizeof (*header)); + header->name = name; + header->value = vec_new (u8, value_len); + clib_memcpy (header->value, value_start, value_len); + vec_terminate_c_string (header->value); + hash_set_mem (ht->value_by_name, header->name, header - ht->headers); + } + while (pos != end); + + *header_table = ht; + + return 0; +} + +/** + * Try to find given header name in header table. + * + * @param header_table Header table to search. + * @param name Header name to match. + * + * @return Header's value in case of success, @c 0 otherwise. + */ +always_inline const char * +http_get_header (http_header_table_t *header_table, const char *name) +{ + uword *p; + http_header_t *header; + + p = hash_get_mem (header_table->value_by_name, name); + if (p) + { + header = vec_elt_at_index (header_table->headers, p[0]); + return (const char *) header->value; + } + + return 0; } #endif /* SRC_PLUGINS_HTTP_HTTP_H_ */ diff --git a/src/plugins/http/http_plugin.rst b/src/plugins/http/http_plugin.rst new file mode 100644 index 00000000000..c4c4d2c8234 --- /dev/null +++ b/src/plugins/http/http_plugin.rst @@ -0,0 +1,156 @@ +.. _http_plugin: + +.. toctree:: + +HTTP Plugin +=========== + +Overview +-------- + +This plugin adds the HTTP protocol to VPP's Host Stack. +As a result parsing of HTTP/1 request or response is available for internal VPP applications. + +Usage +----- + +The plugin exposes following inline functions: ``http_validate_abs_path_syntax``, ``http_validate_query_syntax``, +``http_percent_decode``, ``http_path_remove_dot_segments``, ``http_parse_headers``, ``http_get_header``, +``http_free_header_table``. + +It relies on the hoststack constructs and uses ``http_msg_data_t`` data structure for passing metadata to/from applications. + +Server application +^^^^^^^^^^^^^^^^^^ + +Server application sets ``TRANSPORT_PROTO_HTTP`` as ``transport_proto`` in session endpoint configuration when registering to listen. + +Receiving data +"""""""""""""" + +HTTP plugin sends message header with metadata for parsing, in form of offset and length, followed by all data bytes as received from transport. + +Application will get pre-parsed following items: + +* HTTP method +* target form +* target path offset and length +* target query offset and length +* header section offset and length +* body offset and length + +The example below reads HTTP message header in ``builtin_app_rx_callback``, which is first step application should do: + +.. code-block:: C + + #include <http/http.h> + http_msg_t msg; + rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg); + ASSERT (rv == sizeof (msg)); + +As next step application might validate message and method type, for example application only expects to receive GET requests: + +.. code-block:: C + + if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_GET) + { + /* your error handling */ + } + +Now application can start reading HTTP data. First let's read the target path: + +.. code-block:: C + + u8 *target_path; + vec_validate (target_path, msg.data.target_path_len - 1); + rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset, msg.data.target_path_len, target_path); + ASSERT (rv == msg.data.target_path_len); + +Application might also want to know target form which is stored in ``msg.data.target_form``, you can read more about target forms in RFC9112 section 3.2. +In case of origin form HTTP plugin always sets ``target_path_offset`` after leading slash character. + +Example bellow validates "absolute-path" rule, as described in RFC9110 section 4.1, in case of target in origin form, additionally application can get information if percent encoding is used and decode path: + +.. code-block:: C + + int is_encoded = 0; + if (msg.data.target_form == HTTP_TARGET_ORIGIN_FORM) + { + if (http_validate_abs_path_syntax (target_path, &is_encoded)) + { + /* your error handling */ + } + if (is_encoded) + { + u8 *decoded = http_percent_decode (target_path); + vec_free (target_path); + target_path = decoded; + } + } + +More on topic when to decode in RFC3986 section 2.4. + +When application serves static files, it is highly recommended to sanitize target path by removing dot segments (you don't want to risk path traversal attack): + +.. code-block:: C + + u8 *sanitized_path; + sanitized_path = http_path_remove_dot_segments (target_path); + +Let's move to target query which is optional. Percent encoding might be used too, but we skip it for brevity: + +.. code-block:: C + + u8 *target_query = 0; + if (msg.data.target_query_len) + { + vec_validate (target_query, msg.data.target_query_len - 1); + rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_query_offset, + msg.data.target_query_len, target_query); + ASSERT (rv == msg.data.target_query_len); + if (http_validate_query_syntax (target_query, 0)) + { + /* your error handling */ + } + } + +And now for something completely different, headers. +Headers are parsed using a generic algorithm, independent of the individual header names. +When header is repeated, its combined value consists of all values separated by comma, concatenated in order as received. +Following example shows how to parse headers: + +.. code-block:: C + + if (msg.data.headers_len) + { + u8 *headers = 0; + http_header_table_t *ht; + vec_validate (headers, msg.data.headers_len - 1); + rv = svm_fifo_peek (ts->rx_fifo, msg.data.headers_offset, + msg.data.headers_len, headers); + ASSERT (rv == msg.data.headers_len); + if (http_parse_headers (headers, &ht)) + { + /* your error handling */ + } + /* get Accept header */ + const char *accept_value = http_get_header (ht, HTTP_HEADER_ACCEPT); + if (accept_value) + { + /* do something interesting */ + } + http_free_header_table (ht); + vec_free (headers); + } + +Finally application reads body: + +.. code-block:: C + + u8 *body = 0; + if (msg.data.body_len) + { + vec_validate (body, msg.data.body_len - 1); + rv = svm_fifo_peek (ts->rx_fifo, msg.data.body_offset, msg.data.body_len, body); + ASSERT (rv == msg.data.body_len); + } diff --git a/src/plugins/http_static/builtinurl/json_urls.c b/src/plugins/http_static/builtinurl/json_urls.c index 808893aac79..8578be147f3 100644 --- a/src/plugins/http_static/builtinurl/json_urls.c +++ b/src/plugins/http_static/builtinurl/json_urls.c @@ -20,9 +20,27 @@ hss_url_handler_rc_t handle_get_version (hss_url_handler_args_t *args) { u8 *s = 0; + unformat_input_t input; + int verbose = 0; + + if (args->query) + { + unformat_init_vector (&input, args->query); + if (unformat (&input, "verbose=")) + { + if (unformat (&input, "true")) + verbose = 1; + } + } s = format (s, "{\"vpp_details\": {"); s = format (s, " \"version\": \"%s\",", VPP_BUILD_VER); + if (verbose) + { + s = format (s, " \"build_by\": \"%s\",", VPP_BUILD_USER); + s = format (s, " \"build_host\": \"%s\",", VPP_BUILD_HOST); + s = format (s, " \"build_dir\": \"%s\",", VPP_BUILD_TOPDIR); + } s = format (s, " \"build_date\": \"%s\"}}\r\n", VPP_BUILD_DATE); args->data = s; @@ -31,66 +49,38 @@ handle_get_version (hss_url_handler_args_t *args) return HSS_URL_HANDLER_OK; } -void -trim_path_from_request (u8 *s, char *path) -{ - u8 *cp; - int trim_length = strlen (path) + 1 /* remove '?' */; - - /* Get rid of the path and question-mark */ - vec_delete (s, trim_length, 0); - - /* Tail trim irrelevant browser info */ - cp = s; - while ((cp - s) < vec_len (s)) - { - if (*cp == ' ') - { - /* - * Makes request a vector which happens to look - * like a c-string. - */ - *cp = 0; - vec_set_len (s, cp - s); - break; - } - cp++; - } -} - hss_url_handler_rc_t handle_get_interface_stats (hss_url_handler_args_t *args) { u8 *s = 0, *stats = 0; - uword *p; - u32 *sw_if_indices = 0; + u32 sw_if_index, *sw_if_indices = 0; vnet_hw_interface_t *hi; vnet_sw_interface_t *si; char *q = "\""; int i; int need_comma = 0; + unformat_input_t input; u8 *format_vnet_sw_interface_cntrs (u8 * s, vnet_interface_main_t * im, vnet_sw_interface_t * si, int json); vnet_main_t *vnm = vnet_get_main (); vnet_interface_main_t *im = &vnm->interface_main; /* Get stats for a single interface via http POST */ - if (args->reqtype == HTTP_REQ_POST) + if (args->req_type == HTTP_REQ_POST) { - trim_path_from_request (args->request, "interface_stats.json"); - + unformat_init_vector (&input, args->req_data); /* Find the sw_if_index */ - p = hash_get (im->hw_interface_by_name, args->request); - if (!p) + if (!unformat (&input, "%U", unformat_vnet_sw_interface, vnm, + &sw_if_index)) { s = format (s, "{\"interface_stats\": {[\n"); - s = format (s, " \"name\": \"%s\",", args->request); + s = format (s, " \"name\": \"%s\",", args->req_data); s = format (s, " \"error\": \"%s\"", "UnknownInterface"); s = format (s, "]}\n"); goto out; } - vec_add1 (sw_if_indices, p[0]); + vec_add1 (sw_if_indices, sw_if_index); } else /* default, HTTP_BUILTIN_METHOD_GET */ { diff --git a/src/plugins/http_static/http_static.h b/src/plugins/http_static/http_static.h index 2850d356b74..8f336e83d58 100644 --- a/src/plugins/http_static/http_static.h +++ b/src/plugins/http_static/http_static.h @@ -79,8 +79,9 @@ typedef struct hss_url_handler_args_ /* Request args */ struct { - u8 *request; - http_req_method_t reqtype; + u8 *query; + u8 *req_data; + http_req_method_t req_type; }; /* Reply args */ diff --git a/src/plugins/http_static/static_server.c b/src/plugins/http_static/static_server.c index 040cdca9d7a..c4284a45999 100644 --- a/src/plugins/http_static/static_server.c +++ b/src/plugins/http_static/static_server.c @@ -212,7 +212,7 @@ content_type_from_request (u8 *request) static int try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, - u8 *request) + u8 *target_path, u8 *target_query, u8 *data) { http_status_code_t sc = HTTP_STATUS_OK; hss_url_handler_args_t args = {}; @@ -220,22 +220,22 @@ try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, http_content_type_t type; int rv; - if (!hsm->enable_url_handlers || !request) + if (!hsm->enable_url_handlers || !target_path) return -1; /* zero-length? try "index.html" */ - if (vec_len (request) == 0) + if (vec_len (target_path) == 0) { - request = format (request, "index.html"); + target_path = format (target_path, "index.html"); } - type = content_type_from_request (request); + type = content_type_from_request (target_path); /* Look for built-in GET / POST handlers */ url_table = (rt == HTTP_REQ_GET) ? hsm->get_url_handlers : hsm->post_url_handlers; - p = hash_get_mem (url_table, request); + p = hash_get_mem (url_table, target_path); if (!p) return -1; @@ -244,10 +244,12 @@ try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, hs->cache_pool_index = ~0; if (hsm->debug_level > 0) - clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST", request); + clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST", + target_path); - args.reqtype = rt; - args.request = request; + args.req_type = rt; + args.query = target_query; + args.req_data = data; args.sh.thread_index = hs->thread_index; args.sh.session_index = hs->session_index; @@ -260,7 +262,7 @@ try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, if (rv == HSS_URL_HANDLER_ERROR) { clib_warning ("builtin handler %llx hit on %s '%s' but failed!", p[0], - (rt == HTTP_REQ_GET) ? "GET" : "POST", request); + (rt == HTTP_REQ_GET) ? "GET" : "POST", target_path); sc = HTTP_STATUS_NOT_FOUND; } @@ -354,10 +356,10 @@ try_index_file (hss_main_t *hsm, hss_session_t *hs, u8 *path) static int try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, - u8 *request) + u8 *target) { http_status_code_t sc = HTTP_STATUS_OK; - u8 *path; + u8 *path, *sanitized_path; u32 ce_index; http_content_type_t type; @@ -365,18 +367,18 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, if (!hsm->www_root) return -1; - type = content_type_from_request (request); + type = content_type_from_request (target); + + /* Remove dot segments to prevent path traversal */ + sanitized_path = http_path_remove_dot_segments (target); /* * Construct the file to open - * Browsers are capable of sporadically including a leading '/' */ - if (!request) + if (!target) path = format (0, "%s%c", hsm->www_root, 0); - else if (request[0] == '/') - path = format (0, "%s%s%c", hsm->www_root, request, 0); else - path = format (0, "%s/%s%c", hsm->www_root, request, 0); + path = format (0, "%s/%s%c", hsm->www_root, sanitized_path, 0); if (hsm->debug_level > 0) clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST", path); @@ -419,7 +421,7 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, hs->cache_pool_index = ce_index; done: - + vec_free (sanitized_path); hs->content_type = type; start_send_data (hs, sc); if (!hs->data) @@ -428,33 +430,33 @@ done: return 0; } -static int -handle_request (hss_session_t *hs, http_req_method_t rt, u8 *request) +static void +handle_request (hss_session_t *hs, http_req_method_t rt, u8 *target_path, + u8 *target_query, u8 *data) { hss_main_t *hsm = &hss_main; - if (!try_url_handler (hsm, hs, rt, request)) - return 0; + if (!try_url_handler (hsm, hs, rt, target_path, target_query, data)) + return; - if (!try_file_handler (hsm, hs, rt, request)) - return 0; + if (!try_file_handler (hsm, hs, rt, target_path)) + return; /* Handler did not find anything return 404 */ start_send_data (hs, HTTP_STATUS_NOT_FOUND); hss_session_disconnect_transport (hs); - - return 0; } static int hss_ts_rx_callback (session_t *ts) { hss_session_t *hs; - u8 *request = 0; + u8 *target_path = 0, *target_query = 0, *data = 0; http_msg_t msg; int rv; hs = hss_session_get (ts->thread_index, ts->opaque); + hs->data = 0; /* Read the http message header */ rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg); @@ -463,26 +465,63 @@ hss_ts_rx_callback (session_t *ts) if (msg.type != HTTP_MSG_REQUEST || (msg.method_type != HTTP_REQ_GET && msg.method_type != HTTP_REQ_POST)) { - hs->data = 0; start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED); - return 0; + goto done; } - /* Read request */ - if (msg.data.len) + if (msg.data.target_form != HTTP_TARGET_ORIGIN_FORM) { - vec_validate (request, msg.data.len - 1); - rv = svm_fifo_dequeue (ts->rx_fifo, msg.data.len, request); - ASSERT (rv == msg.data.len); - /* request must be a proper C-string in addition to a vector */ - vec_add1 (request, 0); + start_send_data (hs, HTTP_STATUS_BAD_REQUEST); + goto done; } - /* Find and send data */ - handle_request (hs, msg.method_type, request); + /* Read target path */ + if (msg.data.target_path_len) + { + vec_validate (target_path, msg.data.target_path_len - 1); + rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset, + msg.data.target_path_len, target_path); + ASSERT (rv == msg.data.target_path_len); + if (http_validate_abs_path_syntax (target_path, 0)) + { + start_send_data (hs, HTTP_STATUS_BAD_REQUEST); + goto done; + } + /* Target path must be a proper C-string in addition to a vector */ + vec_add1 (target_path, 0); + } + + /* Read target query */ + if (msg.data.target_query_len) + { + vec_validate (target_query, msg.data.target_query_len - 1); + rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_query_offset, + msg.data.target_query_len, target_query); + ASSERT (rv == msg.data.target_query_len); + if (http_validate_query_syntax (target_query, 0)) + { + start_send_data (hs, HTTP_STATUS_BAD_REQUEST); + goto done; + } + } + + /* Read body */ + if (msg.data.body_len) + { + vec_validate (data, msg.data.body_len - 1); + rv = svm_fifo_peek (ts->rx_fifo, msg.data.body_offset, msg.data.body_len, + data); + ASSERT (rv == msg.data.body_len); + } - vec_free (request); + /* Find and send data */ + handle_request (hs, msg.method_type, target_path, target_query, data); +done: + vec_free (target_path); + vec_free (target_query); + vec_free (data); + svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.len); return 0; } diff --git a/src/plugins/ikev2/ikev2_api.c b/src/plugins/ikev2/ikev2_api.c index a3e71668126..c9608aa660b 100644 --- a/src/plugins/ikev2/ikev2_api.c +++ b/src/plugins/ikev2/ikev2_api.c @@ -173,7 +173,7 @@ send_profile (ikev2_profile_t * profile, vl_api_registration_t * reg, rmp->profile.lifetime_jitter = profile->lifetime_jitter; rmp->profile.handover = profile->handover; - vl_api_ikev2_profile_t_endian (&rmp->profile); + vl_api_ikev2_profile_t_endian (&rmp->profile, 1 /* to network */); vl_api_send_msg (reg, (u8 *) rmp); } @@ -291,7 +291,7 @@ send_sa (ikev2_sa_t * sa, vl_api_ikev2_sa_dump_t * mp, u32 api_sa_index) ikev2_copy_stats (&rsa->stats, &sa->stats); - vl_api_ikev2_sa_t_endian(rsa); + vl_api_ikev2_sa_t_endian (rsa, 1 /* to network */); }); } @@ -382,7 +382,7 @@ send_sa_v2 (ikev2_sa_t *sa, vl_api_ikev2_sa_v2_dump_t *mp, u32 api_sa_index) ikev2_copy_stats (&rsa->stats, &sa->stats); - vl_api_ikev2_sa_v2_t_endian (rsa); + vl_api_ikev2_sa_v2_t_endian (rsa, 1 /* to network */); }); } @@ -476,7 +476,7 @@ send_sa_v3 (ikev2_sa_t *sa, vl_api_ikev2_sa_v3_dump_t *mp, u32 api_sa_index) ikev2_copy_stats (&rsa->stats, &sa->stats); - vl_api_ikev2_sa_v3_t_endian (rsa); + vl_api_ikev2_sa_v3_t_endian (rsa, 1 /* to network */); }); } @@ -549,7 +549,7 @@ send_child_sa (ikev2_child_sa_t * child, k->sk_ar_len); } - vl_api_ikev2_child_sa_t_endian (&rmp->child_sa); + vl_api_ikev2_child_sa_t_endian (&rmp->child_sa, 1 /* to network */); }); } @@ -628,7 +628,7 @@ send_child_sa_v2 (ikev2_child_sa_t *child, vl_api_ikev2_child_sa_v2_dump_t *mp, clib_memcpy (&k->sk_ar, child->sk_ar, k->sk_ar_len); } - vl_api_ikev2_child_sa_v2_t_endian (&rmp->child_sa); + vl_api_ikev2_child_sa_v2_t_endian (&rmp->child_sa, 1 /* to network */); }); } @@ -700,7 +700,7 @@ static void rmp->ts.sa_index = api_sa_index; rmp->ts.child_sa_index = child_sa_index; cp_ts (&rmp->ts, ts, mp->is_initiator); - vl_api_ikev2_ts_t_endian (&rmp->ts); + vl_api_ikev2_ts_t_endian (&rmp->ts, 1 /* to network */); }); } } diff --git a/src/plugins/ikev2/ikev2_test.c b/src/plugins/ikev2/ikev2_test.c index 5682d7058f6..93683a5b5dc 100644 --- a/src/plugins/ikev2/ikev2_test.c +++ b/src/plugins/ikev2/ikev2_test.c @@ -391,7 +391,7 @@ vl_api_ikev2_sa_details_t_handler (vl_api_ikev2_sa_details_t * mp) ip_address_t iaddr; ip_address_t raddr; vl_api_ikev2_keys_t *k = &sa->keys; - vl_api_ikev2_sa_t_endian (sa); + vl_api_ikev2_sa_t_endian (sa, 0 /* from network */); ip_address_decode2 (&sa->iaddr, &iaddr); ip_address_decode2 (&sa->raddr, &raddr); @@ -461,7 +461,7 @@ vl_api_ikev2_sa_v2_details_t_handler (vl_api_ikev2_sa_v2_details_t *mp) ip_address_t iaddr; ip_address_t raddr; vl_api_ikev2_keys_t *k = &sa->keys; - vl_api_ikev2_sa_v2_t_endian (sa); + vl_api_ikev2_sa_v2_t_endian (sa, 0 /* from network */); ip_address_decode2 (&sa->iaddr, &iaddr); ip_address_decode2 (&sa->raddr, &raddr); @@ -533,7 +533,7 @@ vl_api_ikev2_sa_v3_details_t_handler (vl_api_ikev2_sa_v3_details_t *mp) ip_address_t iaddr; ip_address_t raddr; vl_api_ikev2_keys_t *k = &sa->keys; - vl_api_ikev2_sa_v3_t_endian (sa); + vl_api_ikev2_sa_v3_t_endian (sa, 0 /* from network */); ip_address_decode2 (&sa->iaddr, &iaddr); ip_address_decode2 (&sa->raddr, &raddr); @@ -619,7 +619,7 @@ vl_api_ikev2_child_sa_details_t_handler (vl_api_ikev2_child_sa_details_t * mp) vat_main_t *vam = ikev2_test_main.vat_main; vl_api_ikev2_child_sa_t *child_sa = &mp->child_sa; vl_api_ikev2_keys_t *k = &child_sa->keys; - vl_api_ikev2_child_sa_t_endian (child_sa); + vl_api_ikev2_child_sa_t_endian (child_sa, 0 /* from network */); fformat (vam->ofp, " child sa %u:\n", child_sa->child_sa_index); @@ -696,7 +696,7 @@ vl_api_ikev2_child_sa_v2_details_t_handler ( vat_main_t *vam = ikev2_test_main.vat_main; vl_api_ikev2_child_sa_t *child_sa = &mp->child_sa; vl_api_ikev2_keys_t *k = &child_sa->keys; - vl_api_ikev2_child_sa_t_endian (child_sa); + vl_api_ikev2_child_sa_t_endian (child_sa, 0 /* from network */); fformat (vam->ofp, " child sa %u:\n", child_sa->child_sa_index); @@ -784,7 +784,7 @@ static void vat_main_t *vam = ikev2_test_main.vat_main; vl_api_ikev2_ts_t *ts = &mp->ts; ip_address_t start_addr, end_addr; - vl_api_ikev2_ts_t_endian (ts); + vl_api_ikev2_ts_t_endian (ts, 0 /* from network */); ip_address_decode2 (&ts->start_addr, &start_addr); ip_address_decode2 (&ts->end_addr, &end_addr); diff --git a/src/plugins/marvell/pp2/cli.c b/src/plugins/marvell/pp2/cli.c index f4ecb1873c9..5072a3c035b 100644 --- a/src/plugins/marvell/pp2/cli.c +++ b/src/plugins/marvell/pp2/cli.c @@ -31,7 +31,7 @@ mrvl_pp2_create_command_fn (vlib_main_t * vm, unformat_input_t * input, { unformat_input_t _line_input, *line_input = &_line_input; mrvl_pp2_create_if_args_t args = { 0 }; - uint val; + unsigned int val; /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) diff --git a/src/plugins/nat/nat44-ed/nat44_ed_api.c b/src/plugins/nat/nat44-ed/nat44_ed_api.c index 1f01410afce..b6c9d51d777 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed_api.c +++ b/src/plugins/nat/nat44-ed/nat44_ed_api.c @@ -442,7 +442,8 @@ send_nat44_ed_output_interface_details (u32 index, vl_api_registration_t *rp, /* Endian hack until apigen registers _details * endian functions */ - vl_api_nat44_ed_output_interface_details_t_endian (rmp); + vl_api_nat44_ed_output_interface_details_t_endian (rmp, + 1 /* to network */); rmp->_vl_msg_id = htons (rmp->_vl_msg_id); rmp->context = htonl (rmp->context); })); diff --git a/src/plugins/nat/nat44-ei/nat44_ei_api.c b/src/plugins/nat/nat44-ei/nat44_ei_api.c index 8671a556929..454a5032c6a 100644 --- a/src/plugins/nat/nat44-ei/nat44_ei_api.c +++ b/src/plugins/nat/nat44-ei/nat44_ei_api.c @@ -751,7 +751,8 @@ send_nat44_ei_output_interface_details (u32 index, vl_api_registration_t *rp, /* Endian hack until apigen registers _details * endian functions */ - vl_api_nat44_ei_output_interface_details_t_endian (rmp); + vl_api_nat44_ei_output_interface_details_t_endian (rmp, + 1 /* to network */); rmp->_vl_msg_id = htons (rmp->_vl_msg_id); rmp->context = htonl (rmp->context); })); diff --git a/src/plugins/nat/nat44-ei/nat44_ei_in2out.c b/src/plugins/nat/nat44-ei/nat44_ei_in2out.c index 01b333a5234..3b981d69986 100644 --- a/src/plugins/nat/nat44-ei/nat44_ei_in2out.c +++ b/src/plugins/nat/nat44-ei/nat44_ei_in2out.c @@ -859,7 +859,7 @@ nat44_ei_icmp_in2out (vlib_buffer_t *b0, ip4_header_t *ip0, nat44_ei_main_t *nm = &nat44_ei_main; vlib_main_t *vm = vlib_get_main (); ip4_address_t addr; - u16 port; + u16 port = 0; u32 fib_index; nat_protocol_t proto; icmp_echo_header_t *echo0, *inner_echo0 = 0; diff --git a/src/plugins/nat/pnat/pnat_api.c b/src/plugins/nat/pnat/pnat_api.c index 02e61219d1e..a4e7ff192bf 100644 --- a/src/plugins/nat/pnat/pnat_api.c +++ b/src/plugins/nat/pnat/pnat_api.c @@ -116,7 +116,8 @@ static void send_bindings_details(u32 index, vl_api_registration_t *rp, /* Endian hack until apigen registers _details * endian functions */ - vl_api_pnat_bindings_details_t_endian(rmp); + vl_api_pnat_bindings_details_t_endian( + rmp, 1 /* to network */); rmp->_vl_msg_id = htons(rmp->_vl_msg_id); rmp->context = htonl(rmp->context); })); @@ -158,7 +159,7 @@ static void send_interfaces_details(u32 index, vl_api_registration_t *rp, /* Endian hack until apigen registers _details * endian functions */ - vl_api_pnat_interfaces_details_t_endian(rmp); + vl_api_pnat_interfaces_details_t_endian(rmp, 1 /* to network */); rmp->_vl_msg_id = htons(rmp->_vl_msg_id); rmp->context = htonl(rmp->context); })); diff --git a/src/plugins/netmap/CMakeLists.txt b/src/plugins/netmap/CMakeLists.txt new file mode 100644 index 00000000000..d53a9e0911a --- /dev/null +++ b/src/plugins/netmap/CMakeLists.txt @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2024 Tom Jones <thj@freebsd.org> +# +# This software was developed by Tom Jones <thj@freebsd.org> under sponsorship +# from the FreeBSD Foundation. +# + +if (NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") + message(WARNING "Netmap is only currently support on FreeBSD - netmap plugin disabled") + return() +endif() + +add_vpp_plugin(netmap + SOURCES + plugin.c + netmap.c + node.c + device.c + cli.c + netmap_api.c + + MULTIARCH_SOURCES + node.c + device.c + + INSTALL_HEADERS + netmap.h + net_netmap.h + + API_FILES + netmap.api +) diff --git a/extras/deprecated/netmap/FEATURE.yaml b/src/plugins/netmap/FEATURE.yaml index e23e5c243e7..a9dfb2163e4 100644 --- a/extras/deprecated/netmap/FEATURE.yaml +++ b/src/plugins/netmap/FEATURE.yaml @@ -1,11 +1,11 @@ --- name: Netmap Device -maintainer: Damjan Marion <damarion@cisco.com> +maintainer: Tom Jones <thj@freebsd.org> features: - L4 checksum offload description: "Create a netmap interface, which is a high speed user-space - interface that allows VPP to patch into a linux namespace, - a linux container, or a physical NIC without the use of DPDK." + interface that allows VPP to patch to a physical or virtual NIC + without the use of DPDK" missing: - API dump state: production diff --git a/extras/deprecated/netmap/cli.c b/src/plugins/netmap/cli.c index 713632947a1..b54d397ecbe 100644 --- a/extras/deprecated/netmap/cli.c +++ b/src/plugins/netmap/cli.c @@ -22,8 +22,8 @@ #include <vlib/unix/unix.h> #include <vnet/ethernet/ethernet.h> -#include <vnet/devices/netmap/net_netmap.h> -#include <vnet/devices/netmap/netmap.h> +#include <netmap/net_netmap.h> +#include <netmap/netmap.h> static clib_error_t * netmap_create_command_fn (vlib_main_t * vm, unformat_input_t * input, diff --git a/extras/deprecated/netmap/device.c b/src/plugins/netmap/device.c index 47407aaaa26..505deb988c4 100644 --- a/extras/deprecated/netmap/device.c +++ b/src/plugins/netmap/device.c @@ -23,8 +23,8 @@ #include <vlib/unix/unix.h> #include <vnet/ethernet/ethernet.h> -#include <vnet/devices/netmap/net_netmap.h> -#include <vnet/devices/netmap/netmap.h> +#include <netmap/net_netmap.h> +#include <netmap/netmap.h> #define foreach_netmap_tx_func_error \ _(NO_FREE_SLOTS, "no free tx slots") \ diff --git a/extras/deprecated/netmap/net_netmap.h b/src/plugins/netmap/net_netmap.h index fd4253b7c0c..ecccedd4484 100644 --- a/extras/deprecated/netmap/net_netmap.h +++ b/src/plugins/netmap/net_netmap.h @@ -39,10 +39,10 @@ #ifndef _NET_NETMAP_H_ #define _NET_NETMAP_H_ -#define NETMAP_API 11 /* current API version */ +#define NETMAP_API 14 /* current API version */ -#define NETMAP_MIN_API 11 /* min and max versions accepted */ -#define NETMAP_MAX_API 15 +#define NETMAP_MIN_API 14 /* min and max versions accepted */ +#define NETMAP_MAX_API 15 /* * Some fields should be cache-aligned to reduce contention. * The alignment is architecture and OS dependent, but rather than diff --git a/extras/deprecated/netmap/netmap.api b/src/plugins/netmap/netmap.api index a14753cad9c..a14753cad9c 100644 --- a/extras/deprecated/netmap/netmap.api +++ b/src/plugins/netmap/netmap.api diff --git a/extras/deprecated/netmap/netmap.c b/src/plugins/netmap/netmap.c index 7cab6bb0289..ebef215eb3b 100644 --- a/extras/deprecated/netmap/netmap.c +++ b/src/plugins/netmap/netmap.c @@ -20,22 +20,17 @@ #include <sys/ioctl.h> #include <sys/types.h> #include <fcntl.h> -#include <vnet/devices/netmap/net_netmap.h> #include <vlib/vlib.h> #include <vlib/unix/unix.h> #include <vnet/ethernet/ethernet.h> -#include <vnet/devices/netmap/netmap.h> -netmap_main_t netmap_main; +#include <netmap/net_netmap.h> +#include <netmap/netmap.h> +#include <netmap/netmap.api_enum.h> +#include <netmap/netmap.api_types.h> -static u32 -netmap_eth_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi, - u32 flags) -{ - /* nothing for now */ - return 0; -} +netmap_main_t netmap_main; static clib_error_t * netmap_fd_read_ready (clib_file_t * uf) @@ -88,12 +83,11 @@ int netmap_worker_thread_enable () { /* if worker threads are enabled, switch to polling mode */ - foreach_vlib_main (( - { - vlib_node_set_state (this_vlib_main, - netmap_input_node.index, - VLIB_NODE_STATE_POLLING); - })); + foreach_vlib_main () + { + vlib_node_set_state (this_vlib_main, netmap_input_node.index, + VLIB_NODE_STATE_POLLING); + } return 0; } @@ -101,12 +95,11 @@ netmap_worker_thread_enable () int netmap_worker_thread_disable () { - foreach_vlib_main (( - { - vlib_node_set_state (this_vlib_main, - netmap_input_node.index, - VLIB_NODE_STATE_INTERRUPT); - })); + foreach_vlib_main () + { + vlib_node_set_state (this_vlib_main, netmap_input_node.index, + VLIB_NODE_STATE_INTERRUPT); + } return 0; } @@ -117,9 +110,9 @@ netmap_create_if (vlib_main_t * vm, u8 * if_name, u8 * hw_addr_set, { netmap_main_t *nm = &netmap_main; int ret = 0; + uint32_t nr_reg; netmap_if_t *nif = 0; u8 hw_addr[6]; - clib_error_t *error = 0; vnet_sw_interface_t *sw; vnet_main_t *vnm = vnet_get_main (); uword *p; @@ -179,10 +172,39 @@ netmap_create_if (vlib_main_t * vm, u8 * if_name, u8 * hw_addr_set, reg->refcnt++; nif->nifp = NETMAP_IF (reg->mem, req->nr_offset); - nif->first_rx_ring = 0; - nif->last_rx_ring = 0; - nif->first_tx_ring = 0; - nif->last_tx_ring = 0; + nr_reg = nif->req->nr_flags & NR_REG_MASK; + + if (nr_reg == NR_REG_SW) + { /* host stack */ + nif->first_tx_ring = nif->last_tx_ring = nif->req->nr_tx_rings; + nif->first_rx_ring = nif->last_rx_ring = nif->req->nr_rx_rings; + } + else if (nr_reg == NR_REG_ALL_NIC) + { /* only nic */ + nif->first_tx_ring = 0; + nif->first_rx_ring = 0; + nif->last_tx_ring = nif->req->nr_tx_rings - 1; + nif->last_rx_ring = nif->req->nr_rx_rings - 1; + } + else if (nr_reg == NR_REG_NIC_SW) + { + nif->first_tx_ring = 0; + nif->first_rx_ring = 0; + nif->last_tx_ring = nif->req->nr_tx_rings; + nif->last_rx_ring = nif->req->nr_rx_rings; + } + else if (nr_reg == NR_REG_ONE_NIC) + { + /* XXX check validity */ + nif->first_tx_ring = nif->last_tx_ring = nif->first_rx_ring = + nif->last_rx_ring = nif->req->nr_ringid & NETMAP_RING_MASK; + } + else + { /* pipes */ + nif->first_tx_ring = nif->last_tx_ring = 0; + nif->first_rx_ring = nif->last_rx_ring = 0; + } + nif->host_if_name = if_name; nif->per_interface_next_index = ~0; @@ -213,17 +235,14 @@ netmap_create_if (vlib_main_t * vm, u8 * if_name, u8 * hw_addr_set, hw_addr[1] = 0xfe; } - error = ethernet_register_interface (vnm, netmap_device_class.index, - nif->if_index, hw_addr, - &nif->hw_if_index, - netmap_eth_flag_change); + vnet_eth_interface_registration_t eir = {}; - if (error) - { - clib_error_report (error); - ret = VNET_API_ERROR_SYSCALL_ERROR_1; - goto error; - } + eir.dev_class_index = netmap_device_class.index; + eir.dev_instance = nif->if_index; + eir.address = hw_addr; + eir.cb.set_max_frame_size = NULL; + + nif->hw_if_index = vnet_eth_register_interface (vnm, &eir); sw = vnet_get_hw_sw_interface (vnm, nif->hw_if_index); nif->sw_if_index = sw->sw_if_index; diff --git a/extras/deprecated/netmap/netmap.h b/src/plugins/netmap/netmap.h index 29f855fda8e..29f855fda8e 100644 --- a/extras/deprecated/netmap/netmap.h +++ b/src/plugins/netmap/netmap.h diff --git a/extras/deprecated/netmap/netmap_api.c b/src/plugins/netmap/netmap_api.c index ee05ec22d25..51f572a23e6 100644 --- a/extras/deprecated/netmap/netmap_api.c +++ b/src/plugins/netmap/netmap_api.c @@ -22,23 +22,11 @@ #include <vnet/interface.h> #include <vnet/api_errno.h> -#include <vnet/devices/netmap/netmap.h> +#include <netmap/netmap.h> -#include <vnet/vnet_msg_enum.h> - -#define vl_typedefs /* define message structures */ -#include <vnet/vnet_all_api_h.h> -#undef vl_typedefs - -#define vl_endianfun /* define message structures */ -#include <vnet/vnet_all_api_h.h> -#undef vl_endianfun - -/* instantiate all the print functions we know about */ -#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) -#define vl_printfun -#include <vnet/vnet_all_api_h.h> -#undef vl_printfun +#include <vnet/format_fns.h> +#include <netmap/netmap.api_enum.h> +#include <netmap/netmap.api_types.h> #include <vlibapi/api_helper_macros.h> @@ -84,44 +72,14 @@ vl_api_netmap_delete_t_handler (vl_api_netmap_delete_t * mp) REPLY_MACRO (VL_API_NETMAP_DELETE_REPLY); } -/* - * netmap_api_hookup - * Add vpe's API message handlers to the table. - * vlib has already mapped shared memory and - * added the client registration handlers. - * See .../vlib-api/vlibmemory/memclnt_vlib.c:memclnt_process() - */ -#define vl_msg_name_crc_list -#include <vnet/vnet_all_api_h.h> -#undef vl_msg_name_crc_list - -static void -setup_message_id_table (api_main_t * am) -{ -#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id); - foreach_vl_msg_name_crc_netmap; -#undef _ -} - +#include <netmap/netmap.api.c> static clib_error_t * netmap_api_hookup (vlib_main_t * vm) { - api_main_t *am = vlibapi_get_main (); - -#define _(N,n) \ - vl_msg_api_set_handlers(VL_API_##N, #n, \ - vl_api_##n##_t_handler, \ - vl_noop_handler, \ - vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ - sizeof(vl_api_##n##_t), 1); - foreach_vpe_api_msg; -#undef _ - /* * Set up the (msg_name, crc, message-id) table */ - setup_message_id_table (am); + setup_message_id_table (); return 0; } diff --git a/extras/deprecated/netmap/node.c b/src/plugins/netmap/node.c index b0a68824b9c..6169847fa79 100644 --- a/extras/deprecated/netmap/node.c +++ b/src/plugins/netmap/node.c @@ -25,8 +25,8 @@ #include <vnet/devices/devices.h> #include <vnet/feature/feature.h> -#include <vnet/devices/netmap/net_netmap.h> -#include <vnet/devices/netmap/netmap.h> +#include <netmap/net_netmap.h> +#include <netmap/netmap.h> #define foreach_netmap_input_error @@ -112,7 +112,7 @@ netmap_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node, n_free_bufs += vlib_buffer_alloc (vm, &nm->rx_buffers[thread_index][n_free_bufs], VLIB_FRAME_SIZE); - _vec_len (nm->rx_buffers[thread_index]) = n_free_bufs; + vec_set_len (nm->rx_buffers[thread_index], n_free_bufs); } cur_ring = nif->first_rx_ring; @@ -166,7 +166,8 @@ netmap_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node, prev_bi0 = bi0; bi0 = nm->rx_buffers[thread_index][last_empty_buffer]; b0 = vlib_get_buffer (vm, bi0); - _vec_len (nm->rx_buffers[thread_index]) = last_empty_buffer; + vec_set_len (nm->rx_buffers[thread_index], + last_empty_buffer); n_free_bufs--; /* copy data */ @@ -200,11 +201,12 @@ netmap_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node, /* trace */ if (PREDICT_FALSE (n_trace > 0)) { - if (PREDICT_TRUE (first_b0 != 0)) + if (PREDICT_TRUE (first_b0 != 0) && + vlib_trace_buffer (vm, node, next0, first_b0, + /* follow_chain */ 0)) { netmap_input_trace_t *tr; - vlib_trace_buffer (vm, node, next0, first_b0, - /* follow_chain */ 0); + vlib_set_trace_count (vm, node, --n_trace); tr = vlib_add_trace (vm, node, first_b0, sizeof (*tr)); tr->next_index = next0; @@ -213,10 +215,6 @@ netmap_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node, } } - /* redirect if feature path enabled */ - vnet_feature_start_device_input_x1 (nif->sw_if_index, &next0, - first_b0); - /* enque and take next packet */ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, first_bi0, diff --git a/src/plugins/netmap/plugin.c b/src/plugins/netmap/plugin.c new file mode 100644 index 00000000000..1673225b683 --- /dev/null +++ b/src/plugins/netmap/plugin.c @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 Tom Jones <thj@freebsd.org> + * + * This software was developed by Tom Jones <thj@freebsd.org> under sponsorship + * from the FreeBSD Foundation. + * + */ + +#include <vlib/vlib.h> +#include <vnet/plugin/plugin.h> +#include <vpp/app/version.h> + +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "netmap", +}; diff --git a/src/plugins/quic/quic.c b/src/plugins/quic/quic.c index 60d4ac21c19..3f7a3426069 100644 --- a/src/plugins/quic/quic.c +++ b/src/plugins/quic/quic.c @@ -1058,6 +1058,8 @@ quic_on_stream_open (quicly_stream_open_t * self, quicly_stream_t * stream) svm_fifo_add_want_deq_ntf (stream_session->rx_fifo, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL | SVM_FIFO_WANT_DEQ_NOTIF_IF_EMPTY); + svm_fifo_init_ooo_lookup (stream_session->rx_fifo, 0 /* ooo enq */); + svm_fifo_init_ooo_lookup (stream_session->tx_fifo, 1 /* ooo deq */); stream_session->session_state = SESSION_STATE_ACCEPTING; if ((rv = app_worker_accept_notify (app_wrk, stream_session))) @@ -1302,6 +1304,8 @@ quic_connect_stream (session_t * quic_session, session_endpoint_cfg_t * sep) return app_worker_connect_notify (app_wrk, NULL, rv, sep->opaque); } + svm_fifo_init_ooo_lookup (stream_session->rx_fifo, 0 /* ooo enq */); + svm_fifo_init_ooo_lookup (stream_session->tx_fifo, 1 /* ooo deq */); svm_fifo_add_want_deq_ntf (stream_session->rx_fifo, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL | SVM_FIFO_WANT_DEQ_NOTIF_IF_EMPTY); @@ -1679,6 +1683,9 @@ quic_on_quic_session_connected (quic_ctx_t * ctx) return; } + svm_fifo_init_ooo_lookup (quic_session->rx_fifo, 0 /* ooo enq */); + svm_fifo_init_ooo_lookup (quic_session->tx_fifo, 1 /* ooo deq */); + quic_session->session_state = SESSION_STATE_CONNECTING; if ((rv = app_worker_connect_notify (app_wrk, quic_session, SESSION_E_NONE, ctx->client_opaque))) @@ -2137,6 +2144,9 @@ quic_accept_connection (quic_rx_packet_ctx_t * pctx) return; } + svm_fifo_init_ooo_lookup (quic_session->rx_fifo, 0 /* ooo enq */); + svm_fifo_init_ooo_lookup (quic_session->tx_fifo, 1 /* ooo deq */); + app_wrk = app_worker_get (quic_session->app_wrk_index); quic_session->session_state = SESSION_STATE_ACCEPTING; if ((rv = app_worker_accept_notify (app_wrk, quic_session))) diff --git a/src/plugins/srmpls/CMakeLists.txt b/src/plugins/srmpls/CMakeLists.txt new file mode 100644 index 00000000000..25905d31e1b --- /dev/null +++ b/src/plugins/srmpls/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (c) 2024 Cisco and/or its affiliates +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_vpp_plugin(srmpls + SOURCES + sr_mpls_policy.c + sr_mpls_steering.c + sr_mpls_api.c + plugin.c + + INSTALL_HEADERS + sr_mpls.h + + API_FILES + sr_mpls.api + + # This might need to be VAT_AUTO_TEST? Not documented + API_TEST_SOURCES + sr_mpls_test.c +) diff --git a/src/vnet/srmpls/FEATURE.yaml b/src/plugins/srmpls/FEATURE.yaml index c5b958224c7..c5b958224c7 100644 --- a/src/vnet/srmpls/FEATURE.yaml +++ b/src/plugins/srmpls/FEATURE.yaml diff --git a/src/vnet/srmpls/dir.dox b/src/plugins/srmpls/dir.dox index 76ec1d6a41b..76ec1d6a41b 100644 --- a/src/vnet/srmpls/dir.dox +++ b/src/plugins/srmpls/dir.dox diff --git a/extras/deprecated/netmap/dir.dox b/src/plugins/srmpls/plugin.c index 7ddbf947c29..af87607764f 100644 --- a/extras/deprecated/netmap/dir.dox +++ b/src/plugins/srmpls/plugin.c @@ -1,6 +1,7 @@ /* - * Copyright (c) 2017 Cisco and/or its affiliates. + * plugin.c: srmpls * + * Copyright (c) 2024 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -14,14 +15,12 @@ * limitations under the License. */ -/* Doxygen directory documentation */ +#include <vlib/vlib.h> +#include <vnet/plugin/plugin.h> +#include <vpp/app/version.h> -/** -@dir -@brief netmap Interface Implementation. - -This directory contains the source code for the netmap driver. - -*/ -/*? %%clicmd:group_label netmap %% ?*/ -/*? %%syscfg:group_label netmap %% ?*/ +// register a plugin +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "Segment Routing for MPLS plugin", +}; diff --git a/src/vnet/srmpls/sr_doc.rst b/src/plugins/srmpls/sr_doc.rst index ed847fa0d42..ed847fa0d42 100644 --- a/src/vnet/srmpls/sr_doc.rst +++ b/src/plugins/srmpls/sr_doc.rst diff --git a/src/vnet/srmpls/sr_mpls.api b/src/plugins/srmpls/sr_mpls.api index 742f135d493..742f135d493 100644 --- a/src/vnet/srmpls/sr_mpls.api +++ b/src/plugins/srmpls/sr_mpls.api diff --git a/src/vnet/srmpls/sr_mpls.h b/src/plugins/srmpls/sr_mpls.h index a8f9494428f..a8f9494428f 100644 --- a/src/vnet/srmpls/sr_mpls.h +++ b/src/plugins/srmpls/sr_mpls.h diff --git a/src/vnet/srmpls/sr_mpls_api.c b/src/plugins/srmpls/sr_mpls_api.c index 920856acff6..3e89017dbc1 100644 --- a/src/vnet/srmpls/sr_mpls_api.c +++ b/src/plugins/srmpls/sr_mpls_api.c @@ -17,7 +17,7 @@ */ #include <vnet/vnet.h> -#include <vnet/srmpls/sr_mpls.h> +#include "sr_mpls.h" #include <vlibmemory/api.h> #include <vnet/interface.h> @@ -26,28 +26,27 @@ #include <vnet/ip/ip_types_api.h> #include <vnet/format_fns.h> -#include <vnet/srmpls/sr_mpls.api_enum.h> -#include <vnet/srmpls/sr_mpls.api_types.h> - +#include <plugins/srmpls/sr_mpls.api_enum.h> +#include <plugins/srmpls/sr_mpls.api_types.h> #define vl_api_version(n, v) static u32 api_version = v; -#include <vnet/srmpls/sr_mpls.api.h> +#include <plugins/srmpls/sr_mpls.api.h> #undef vl_api_version #define vl_endianfun -#include <vnet/srmpls/sr_mpls.api.h> +#include <plugins/srmpls/sr_mpls.api.h> #undef vl_endianfun #define vl_calcsizefun -#include <vnet/srmpls/sr_mpls.api.h> +#include <plugins/srmpls/sr_mpls.api.h> #undef vl_calcsizefun #define vl_printfun -#include <vnet/srmpls/sr_mpls.api.h> +#include <plugins/srmpls/sr_mpls.api.h> #undef vl_printfun #define vl_msg_name_crc_list -#include <vnet/srmpls/sr_mpls.api.h> +#include <plugins/srmpls/sr_mpls.api.h> #undef vl_msg_name_crc_list #define REPLY_MSG_ID_BASE msg_id_base diff --git a/src/vnet/srmpls/sr_mpls_policy.c b/src/plugins/srmpls/sr_mpls_policy.c index 41cb71601e9..af24acd8cf6 100644 --- a/src/vnet/srmpls/sr_mpls_policy.c +++ b/src/plugins/srmpls/sr_mpls_policy.c @@ -31,7 +31,7 @@ #include <vlib/vlib.h> #include <vnet/vnet.h> -#include <vnet/srmpls/sr_mpls.h> +#include "sr_mpls.h" #include <vnet/fib/mpls_fib.h> #include <vnet/dpo/dpo.h> #include <vnet/ip/ip.h> diff --git a/src/vnet/srmpls/sr_mpls_steering.c b/src/plugins/srmpls/sr_mpls_steering.c index e8920df542b..24c8b0e2d9f 100644 --- a/src/vnet/srmpls/sr_mpls_steering.c +++ b/src/plugins/srmpls/sr_mpls_steering.c @@ -31,7 +31,7 @@ #include <vlib/vlib.h> #include <vnet/vnet.h> -#include <vnet/srmpls/sr_mpls.h> +#include "sr_mpls.h" #include <vnet/ip/ip4_packet.h> #include <vnet/ip/ip6_packet.h> #include <vnet/fib/mpls_fib.h> diff --git a/src/vnet/srmpls/sr_mpls_test.c b/src/plugins/srmpls/sr_mpls_test.c index e5d68462443..7aff4c32b06 100644 --- a/src/vnet/srmpls/sr_mpls_test.c +++ b/src/plugins/srmpls/sr_mpls_test.c @@ -25,11 +25,11 @@ /* Declare message IDs */ #include <vnet/format_fns.h> -#include <vnet/srmpls/sr_mpls.api_enum.h> -#include <vnet/srmpls/sr_mpls.api_types.h> +#include <plugins/srmpls/sr_mpls.api_enum.h> +#include <plugins/srmpls/sr_mpls.api_types.h> #define vl_endianfun /* define message structures */ -#include <vnet/srmpls/sr_mpls.api.h> +#include <plugins/srmpls/sr_mpls.api.h> #undef vl_endianfun typedef struct @@ -163,7 +163,7 @@ api_sr_mpls_policy_del (vat_main_t *vam) return ret; } -#include <vnet/srmpls/sr_mpls.api_test.c> +#include <plugins/srmpls/sr_mpls.api_test.c> /* * fd.io coding-style-patch-verification: ON diff --git a/src/plugins/unittest/gso_test.c b/src/plugins/unittest/gso_test.c index 54eb7422c87..43c614341d2 100644 --- a/src/plugins/unittest/gso_test.c +++ b/src/plugins/unittest/gso_test.c @@ -96,12 +96,94 @@ GSO_TEST_REGISTER_DATA (gso_ipv6_tcp, static) = { .is_ip6 = 1, }; +/* + * this does not support tunnel packets + */ +static void +set_hdr_offsets (vlib_buffer_t *b0, u8 is_l2) +{ + u16 ethertype = 0, l2hdr_sz = 0; + vnet_buffer_oflags_t oflags = 0; + u8 l4_proto = 0; + + if (!is_l2) + { + switch (b0->data[0] & 0xf0) + { + case 0x40: + ethertype = ETHERNET_TYPE_IP4; + break; + case 0x60: + ethertype = ETHERNET_TYPE_IP6; + break; + } + } + else + { + ethernet_header_t *eh = (ethernet_header_t *) b0->data; + ethertype = clib_net_to_host_u16 (eh->type); + l2hdr_sz = sizeof (ethernet_header_t); + + if (ethernet_frame_is_tagged (ethertype)) + { + ethernet_vlan_header_t *vlan = (ethernet_vlan_header_t *) (eh + 1); + + ethertype = clib_net_to_host_u16 (vlan->type); + l2hdr_sz += sizeof (*vlan); + if (ethertype == ETHERNET_TYPE_VLAN) + { + vlan++; + ethertype = clib_net_to_host_u16 (vlan->type); + l2hdr_sz += sizeof (*vlan); + } + } + } + + vnet_buffer (b0)->l2_hdr_offset = 0; + vnet_buffer (b0)->l3_hdr_offset = l2hdr_sz; + + if (PREDICT_TRUE (ethertype == ETHERNET_TYPE_IP4)) + { + ip4_header_t *ip4 = (ip4_header_t *) (b0->data + l2hdr_sz); + vnet_buffer (b0)->l4_hdr_offset = l2hdr_sz + ip4_header_bytes (ip4); + l4_proto = ip4->protocol; + oflags |= VNET_BUFFER_OFFLOAD_F_IP_CKSUM; + b0->flags |= (VNET_BUFFER_F_IS_IP4 | VNET_BUFFER_F_L2_HDR_OFFSET_VALID | + VNET_BUFFER_F_L3_HDR_OFFSET_VALID | + VNET_BUFFER_F_L4_HDR_OFFSET_VALID); + } + else if (PREDICT_TRUE (ethertype == ETHERNET_TYPE_IP6)) + { + ip6_header_t *ip6 = (ip6_header_t *) (b0->data + l2hdr_sz); + vnet_buffer (b0)->l4_hdr_offset = l2hdr_sz + sizeof (ip6_header_t); + /* FIXME IPv6 EH traversal */ + l4_proto = ip6->protocol; + b0->flags |= (VNET_BUFFER_F_IS_IP6 | VNET_BUFFER_F_L2_HDR_OFFSET_VALID | + VNET_BUFFER_F_L3_HDR_OFFSET_VALID | + VNET_BUFFER_F_L4_HDR_OFFSET_VALID); + } + if (l4_proto == IP_PROTOCOL_TCP) + { + oflags |= VNET_BUFFER_OFFLOAD_F_TCP_CKSUM; + } + else if (l4_proto == IP_PROTOCOL_UDP) + { + oflags |= VNET_BUFFER_OFFLOAD_F_UDP_CKSUM; + } + if (oflags) + vnet_buffer_offload_flags_set (b0, oflags); +} + static u32 -fill_buffers (vlib_main_t *vm, u32 *buffer_indices, u8 *data, u32 data_size, - u32 n_buffers, u32 buffer_size, u32 packet_size, u32 gso_size, - u32 l4_hdr_len) +fill_buffers (vlib_main_t *vm, u32 *buffer_indices, + gso_test_data_t *gso_test_data, u32 n_buffers, u32 buffer_size, + u32 packet_size, u32 gso_size) { u32 i; + u8 *data = gso_test_data->data; + u32 data_size = gso_test_data->data_size; + u32 l4_hdr_len = gso_test_data->l4_hdr_len; + u8 is_l2 = gso_test_data->is_l2; for (i = 0; i < n_buffers; i++) { @@ -153,6 +235,8 @@ fill_buffers (vlib_main_t *vm, u32 *buffer_indices, u8 *data, u32 data_size, len += fill_data_size; } while (k < n_bufs); + + set_hdr_offsets (b, is_l2); b->flags |= VNET_BUFFER_F_GSO; vnet_buffer2 (b)->gso_size = gso_size; vnet_buffer2 (b)->gso_l4_hdr_sz = l4_hdr_len; @@ -165,17 +249,14 @@ fill_buffers (vlib_main_t *vm, u32 *buffer_indices, u8 *data, u32 data_size, static_always_inline u32 gso_segment_buffer_test (vlib_main_t *vm, u32 bi, - vnet_interface_per_thread_data_t *ptd, u8 is_l2, - u8 is_ip6) + vnet_interface_per_thread_data_t *ptd, u8 is_l2) { vlib_buffer_t *b = vlib_get_buffer (vm, bi); - generic_header_offset_t gho = { 0 }; u32 n_tx_bytes = 0; if (PREDICT_TRUE (b->flags & VNET_BUFFER_F_GSO)) { - vnet_generic_header_offset_parser (b, &gho, is_l2, !is_ip6, is_ip6); - n_tx_bytes = gso_segment_buffer_inline (vm, ptd, b, &gho, is_l2, is_ip6); + n_tx_bytes = gso_segment_buffer_inline (vm, ptd, b, is_l2); } return n_tx_bytes; @@ -237,19 +318,16 @@ test_gso_perf (vlib_main_t *vm, gso_test_main_t *gtm) vlib_buffer_free (vm, buffer_indices, n_alloc); goto done; } - n_filled = - fill_buffers (vm, buffer_indices, gso_test_data->data, - gso_test_data->data_size, n_buffers, buffer_size, - packet_size, gso_size, gso_test_data->l4_hdr_len); + n_filled = fill_buffers (vm, buffer_indices, gso_test_data, n_buffers, + buffer_size, packet_size, gso_size); u8 is_l2 = gso_test_data->is_l2; - u8 is_ip6 = gso_test_data->is_ip6; for (k = 0; k < warmup_rounds; k++) { for (j = 0; j < n_filled; j++) - gso_segment_buffer_test (vm, buffer_indices[j], &ptd[j], is_l2, - is_ip6); + gso_segment_buffer_test (vm, buffer_indices[j], &ptd[j], is_l2); + for (j = 0; j < n_filled; j++) { vlib_buffer_free (vm, ptd[j].split_buffers, @@ -264,8 +342,9 @@ test_gso_perf (vlib_main_t *vm, gso_test_main_t *gtm) { t0 = clib_cpu_time_now (); for (j = 0; j < n_filled; j++) - gso_segment_buffer_test (vm, buffer_indices[j], &ptd[j], is_l2, - is_ip6); + gso_segment_buffer_test (vm, buffer_indices[j], &ptd[j], + is_l2); + t1 = clib_cpu_time_now (); t2[i] += (t1 - t0); for (j = 0; j < n_filled; j++) diff --git a/src/plugins/unittest/policer_test.c b/src/plugins/unittest/policer_test.c index 2b14bf687bf..41f769960a3 100644 --- a/src/plugins/unittest/policer_test.c +++ b/src/plugins/unittest/policer_test.c @@ -21,7 +21,7 @@ policer_test (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd_arg) { int policer_index, i; - uint rate_kbps, burst, num_pkts; + unsigned int rate_kbps, burst, num_pkts; double total_bytes, cpu_ticks_per_pkt, time = 0; double cpu_speed, cpu_ticks_per_byte; policer_result_e result, input_colour = POLICE_CONFORM; diff --git a/src/plugins/urpf/CMakeLists.txt b/src/plugins/urpf/CMakeLists.txt index 2f44e3b2344..f665d30b0bb 100644 --- a/src/plugins/urpf/CMakeLists.txt +++ b/src/plugins/urpf/CMakeLists.txt @@ -22,6 +22,10 @@ add_vpp_plugin(urpf ip4_urpf.c ip6_urpf.c + INSTALL_HEADERS + urpf_dp.h + urpf.h + API_FILES urpf.api ) diff --git a/src/plugins/urpf/urpf.c b/src/plugins/urpf/urpf.c index e5209caafb4..1e7d6c0fb91 100644 --- a/src/plugins/urpf/urpf.c +++ b/src/plugins/urpf/urpf.c @@ -60,7 +60,17 @@ static const char *urpf_feats[N_AF][VLIB_N_DIR][URPF_N_MODES] = urpf_data_t *urpf_cfgs[N_AF][VLIB_N_DIR]; u8 * -format_urpf_mode (u8 * s, va_list * a) +format_urpf_trace (u8 *s, va_list *va) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *); + urpf_trace_t *t = va_arg (*va, urpf_trace_t *); + + return format (s, "uRPF:%d fib:%d", t->urpf, t->fib_index); +} + +__clib_export u8 * +format_urpf_mode (u8 *s, va_list *a) { urpf_mode_t mode = va_arg (*a, int); @@ -76,8 +86,8 @@ format_urpf_mode (u8 * s, va_list * a) return (format (s, "unknown")); } -static uword -unformat_urpf_mode (unformat_input_t * input, va_list * args) +__clib_export uword +unformat_urpf_mode (unformat_input_t *input, va_list *args) { urpf_mode_t *mode = va_arg (*args, urpf_mode_t *); @@ -94,7 +104,16 @@ unformat_urpf_mode (unformat_input_t * input, va_list * args) return 0; } -int +__clib_export int +urpf_feature_enable_disable (ip_address_family_t af, vlib_dir_t dir, + urpf_mode_t mode, u32 sw_if_index, int enable) +{ + return vnet_feature_enable_disable (urpf_feat_arcs[af][dir], + urpf_feats[af][dir][mode], sw_if_index, + enable, 0, 0); +} + +__clib_export int urpf_update (urpf_mode_t mode, u32 sw_if_index, ip_address_family_t af, vlib_dir_t dir, u32 table_id) { diff --git a/src/plugins/urpf/urpf.h b/src/plugins/urpf/urpf.h index 6983a2b440c..a40a25df16b 100644 --- a/src/plugins/urpf/urpf.h +++ b/src/plugins/urpf/urpf.h @@ -32,7 +32,15 @@ typedef enum urpf_mode_t_ #define URPF_N_MODES (URPF_MODE_STRICT+1) -extern u8 *format_urpf_mode (u8 * s, va_list * a); +typedef struct +{ + index_t urpf; + u32 fib_index; +} urpf_trace_t; + +u8 *format_urpf_trace (u8 *s, va_list *va); +u8 *format_urpf_mode (u8 *s, va_list *a); +uword unformat_urpf_mode (unformat_input_t *input, va_list *args); typedef struct { @@ -43,8 +51,8 @@ typedef struct extern urpf_data_t *urpf_cfgs[N_AF][VLIB_N_DIR]; -extern int urpf_update (urpf_mode_t mode, u32 sw_if_index, - ip_address_family_t af, vlib_dir_t dir, u32 table_id); +int urpf_update (urpf_mode_t mode, u32 sw_if_index, ip_address_family_t af, + vlib_dir_t dir, u32 table_id); #endif diff --git a/src/plugins/urpf/urpf_dp.h b/src/plugins/urpf/urpf_dp.h index 816d8b70b90..b17fed7e04b 100644 --- a/src/plugins/urpf/urpf_dp.h +++ b/src/plugins/urpf/urpf_dp.h @@ -53,22 +53,6 @@ * * This file contains the interface unicast source check. */ -typedef struct -{ - index_t urpf; -} urpf_trace_t; - -static u8 * -format_urpf_trace (u8 * s, va_list * va) -{ - CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *); - CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *); - urpf_trace_t *t = va_arg (*va, urpf_trace_t *); - - s = format (s, "uRPF:%d", t->urpf); - - return s; -} #define foreach_urpf_error \ _(DROP, "uRPF Drop") \ @@ -87,10 +71,157 @@ typedef enum URPF_N_NEXT, } urpf_next_t; +static_always_inline u32 +urpf_get_fib_index (vlib_buffer_t *b, ip_address_family_t af, vlib_dir_t dir) +{ + u32 sw_if_index = vnet_buffer (b)->sw_if_index[dir]; + return vec_elt (urpf_cfgs[af][dir], sw_if_index).fib_index; +} + +static_always_inline void +urpf_perform_check_x1 (ip_address_family_t af, vlib_dir_t dir, + urpf_mode_t mode, vlib_buffer_t *b, const u8 *h, + u32 fib_index, load_balance_t **lb, u32 *pass) +{ + load_balance_t *llb; + u32 lpass; + u32 lb_index; + + ASSERT (fib_index != ~0); + + if (AF_IP4 == af) + { + const ip4_header_t *ip; + + ip = (ip4_header_t *) h; + + lb_index = ip4_fib_forwarding_lookup (fib_index, &ip->src_address); + + /* Pass multicast. */ + lpass = (ip4_address_is_multicast (&ip->src_address) || + ip4_address_is_global_broadcast (&ip->src_address)); + } + else + { + const ip6_header_t *ip; + + ip = (ip6_header_t *) h; + + lb_index = ip6_fib_table_fwding_lookup (fib_index, &ip->src_address); + lpass = ip6_address_is_multicast (&ip->src_address); + } + + llb = load_balance_get (lb_index); + + if (URPF_MODE_STRICT == mode) + { + int res; + + res = fib_urpf_check (llb->lb_urpf, vnet_buffer (b)->sw_if_index[dir]); + if (VLIB_RX == dir) + lpass |= res; + else + { + lpass |= !res && fib_urpf_check_size (llb->lb_urpf); + lpass |= b->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED; + } + } + else + lpass |= fib_urpf_check_size (llb->lb_urpf); + + *lb = llb; + *pass = lpass; +} + +static_always_inline void +urpf_perform_check_x2 (ip_address_family_t af, vlib_dir_t dir, + urpf_mode_t mode, vlib_buffer_t *b0, vlib_buffer_t *b1, + const u8 *h0, const u8 *h1, u32 fib_index0, + u32 fib_index1, load_balance_t **lb0, + load_balance_t **lb1, u32 *pass0, u32 *pass1) +{ + load_balance_t *llb0, *llb1; + u32 lpass0, lpass1; + u32 lb_index0, lb_index1; + + ASSERT (fib_index0 != ~0); + ASSERT (fib_index1 != ~0); + + if (AF_IP4 == af) + { + const ip4_header_t *ip0, *ip1; + + ip0 = (ip4_header_t *) h0; + ip1 = (ip4_header_t *) h1; + + ip4_fib_forwarding_lookup_x2 (fib_index0, fib_index1, &ip0->src_address, + &ip1->src_address, &lb_index0, &lb_index1); + /* Pass multicast. */ + lpass0 = (ip4_address_is_multicast (&ip0->src_address) || + ip4_address_is_global_broadcast (&ip0->src_address)); + lpass1 = (ip4_address_is_multicast (&ip1->src_address) || + ip4_address_is_global_broadcast (&ip1->src_address)); + } + else + { + const ip6_header_t *ip0, *ip1; + + ip0 = (ip6_header_t *) h0; + ip1 = (ip6_header_t *) h1; + + lb_index0 = ip6_fib_table_fwding_lookup (fib_index0, &ip0->src_address); + lb_index1 = ip6_fib_table_fwding_lookup (fib_index1, &ip1->src_address); + lpass0 = ip6_address_is_multicast (&ip0->src_address); + lpass1 = ip6_address_is_multicast (&ip1->src_address); + } + + llb0 = load_balance_get (lb_index0); + llb1 = load_balance_get (lb_index1); + + if (URPF_MODE_STRICT == mode) + { + /* for RX the check is: would this source adddress be + * forwarded out of the interface on which it was recieved, + * if yes allow. For TX it's; would this source address be + * forwarded out of the interface through which it is being + * sent, if yes drop. + */ + int res0, res1; + + res0 = + fib_urpf_check (llb0->lb_urpf, vnet_buffer (b0)->sw_if_index[dir]); + res1 = + fib_urpf_check (llb1->lb_urpf, vnet_buffer (b1)->sw_if_index[dir]); + + if (VLIB_RX == dir) + { + lpass0 |= res0; + lpass1 |= res1; + } + else + { + lpass0 |= !res0 && fib_urpf_check_size (llb0->lb_urpf); + lpass1 |= !res1 && fib_urpf_check_size (llb1->lb_urpf); + + /* allow locally generated */ + lpass0 |= b0->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED; + lpass1 |= b1->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED; + } + } + else + { + lpass0 |= fib_urpf_check_size (llb0->lb_urpf); + lpass1 |= fib_urpf_check_size (llb1->lb_urpf); + } + + *lb0 = llb0; + *lb1 = llb1; + *pass0 = lpass0; + *pass1 = lpass1; +} + static_always_inline uword -urpf_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, +urpf_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, ip_address_family_t af, vlib_dir_t dir, urpf_mode_t mode) { vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; @@ -106,8 +237,8 @@ urpf_inline (vlib_main_t * vm, while (n_left >= 4) { - u32 pass0, lb_index0, pass1, lb_index1; - const load_balance_t *lb0, *lb1; + u32 pass0, pass1; + load_balance_t *lb0 = 0, *lb1 = 0; u32 fib_index0, fib_index1; const u8 *h0, *h1; @@ -121,87 +252,32 @@ urpf_inline (vlib_main_t * vm, h0 = (u8 *) vlib_buffer_get_current (b[0]); h1 = (u8 *) vlib_buffer_get_current (b[1]); - if (VLIB_TX == dir) { h0 += vnet_buffer (b[0])->ip.save_rewrite_length; h1 += vnet_buffer (b[1])->ip.save_rewrite_length; } - fib_index0 = - urpf_cfgs[af][dir][vnet_buffer (b[0])->sw_if_index[dir]].fib_index; - fib_index1 = - urpf_cfgs[af][dir][vnet_buffer (b[1])->sw_if_index[dir]].fib_index; + fib_index0 = urpf_get_fib_index (b[0], af, dir); + fib_index1 = urpf_get_fib_index (b[1], af, dir); + urpf_perform_check_x2 (af, dir, mode, b[0], b[1], h0, h1, fib_index0, + fib_index1, &lb0, &lb1, &pass0, &pass1); - if (AF_IP4 == af) - { - const ip4_header_t *ip0, *ip1; - - ip0 = (ip4_header_t *) h0; - ip1 = (ip4_header_t *) h1; - - ip4_fib_forwarding_lookup_x2 (fib_index0, - fib_index1, - &ip0->src_address, - &ip1->src_address, - &lb_index0, &lb_index1); - /* Pass multicast. */ - pass0 = (ip4_address_is_multicast (&ip0->src_address) || - ip4_address_is_global_broadcast (&ip0->src_address)); - pass1 = (ip4_address_is_multicast (&ip1->src_address) || - ip4_address_is_global_broadcast (&ip1->src_address)); - } - else + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) { - const ip6_header_t *ip0, *ip1; - - ip0 = (ip6_header_t *) h0; - ip1 = (ip6_header_t *) h1; - - lb_index0 = ip6_fib_table_fwding_lookup (fib_index0, - &ip0->src_address); - lb_index1 = ip6_fib_table_fwding_lookup (fib_index1, - &ip1->src_address); - pass0 = ip6_address_is_multicast (&ip0->src_address); - pass1 = ip6_address_is_multicast (&ip1->src_address); - } - - lb0 = load_balance_get (lb_index0); - lb1 = load_balance_get (lb_index1); + urpf_trace_t *t; - if (URPF_MODE_STRICT == mode) - { - /* for RX the check is: would this source adddress be forwarded - * out of the interface on which it was recieved, if yes allow. - * For TX it's; would this source address be forwarded out of the - * interface through which it is being sent, if yes drop. - */ - int res0, res1; - - res0 = fib_urpf_check (lb0->lb_urpf, - vnet_buffer (b[0])->sw_if_index[dir]); - res1 = fib_urpf_check (lb1->lb_urpf, - vnet_buffer (b[1])->sw_if_index[dir]); - - if (VLIB_RX == dir) - { - pass0 |= res0; - pass1 |= res1; - } - else - { - pass0 |= !res0 && fib_urpf_check_size (lb0->lb_urpf); - pass1 |= !res1 && fib_urpf_check_size (lb1->lb_urpf); - - /* allow locally generated */ - pass0 |= b[0]->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED; - pass1 |= b[1]->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED; - } + t = vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->urpf = lb0 ? lb0->lb_urpf : ~0; + t->fib_index = fib_index0; } - else + if (b[1]->flags & VLIB_BUFFER_IS_TRACED) { - pass0 |= fib_urpf_check_size (lb0->lb_urpf); - pass1 |= fib_urpf_check_size (lb1->lb_urpf); + urpf_trace_t *t; + + t = vlib_add_trace (vm, node, b[1], sizeof (*t)); + t->urpf = lb1 ? lb1->lb_urpf : ~0; + t->fib_index = fib_index1; } if (PREDICT_TRUE (pass0)) @@ -218,22 +294,6 @@ urpf_inline (vlib_main_t * vm, next[1] = URPF_NEXT_DROP; b[1]->error = node->errors[URPF_ERROR_DROP]; } - - if (b[0]->flags & VLIB_BUFFER_IS_TRACED) - { - urpf_trace_t *t; - - t = vlib_add_trace (vm, node, b[0], sizeof (*t)); - t->urpf = lb0->lb_urpf; - } - if (b[1]->flags & VLIB_BUFFER_IS_TRACED) - { - urpf_trace_t *t; - - t = vlib_add_trace (vm, node, b[1], sizeof (*t)); - t->urpf = lb1->lb_urpf; - } - b += 2; next += 2; n_left -= 2; @@ -241,8 +301,8 @@ urpf_inline (vlib_main_t * vm, while (n_left) { - u32 pass0, lb_index0, fib_index0; - const load_balance_t *lb0; + u32 pass0, fib_index0; + load_balance_t *lb0 = 0; const u8 *h0; h0 = (u8 *) vlib_buffer_get_current (b[0]); @@ -250,51 +310,18 @@ urpf_inline (vlib_main_t * vm, if (VLIB_TX == dir) h0 += vnet_buffer (b[0])->ip.save_rewrite_length; - fib_index0 = - urpf_cfgs[af][dir][vnet_buffer (b[0])->sw_if_index[dir]].fib_index; - - if (AF_IP4 == af) - { - const ip4_header_t *ip0; - - ip0 = (ip4_header_t *) h0; - - lb_index0 = ip4_fib_forwarding_lookup (fib_index0, - &ip0->src_address); + fib_index0 = urpf_get_fib_index (b[0], af, dir); + urpf_perform_check_x1 (af, dir, mode, b[0], h0, fib_index0, &lb0, + &pass0); - /* Pass multicast. */ - pass0 = (ip4_address_is_multicast (&ip0->src_address) || - ip4_address_is_global_broadcast (&ip0->src_address)); - } - else + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) { - const ip6_header_t *ip0; - - ip0 = (ip6_header_t *) h0; - - lb_index0 = ip6_fib_table_fwding_lookup (fib_index0, - &ip0->src_address); - pass0 = ip6_address_is_multicast (&ip0->src_address); - } - - lb0 = load_balance_get (lb_index0); + urpf_trace_t *t; - if (URPF_MODE_STRICT == mode) - { - int res0; - - res0 = fib_urpf_check (lb0->lb_urpf, - vnet_buffer (b[0])->sw_if_index[dir]); - if (VLIB_RX == dir) - pass0 |= res0; - else - { - pass0 |= !res0 && fib_urpf_check_size (lb0->lb_urpf); - pass0 |= b[0]->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED; - } + t = vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->urpf = lb0 ? lb0->lb_urpf : ~0; + t->fib_index = fib_index0; } - else - pass0 |= fib_urpf_check_size (lb0->lb_urpf); if (PREDICT_TRUE (pass0)) vnet_feature_next_u16 (&next[0], b[0]); @@ -303,14 +330,6 @@ urpf_inline (vlib_main_t * vm, next[0] = URPF_NEXT_DROP; b[0]->error = node->errors[URPF_ERROR_DROP]; } - - if (b[0]->flags & VLIB_BUFFER_IS_TRACED) - { - urpf_trace_t *t; - - t = vlib_add_trace (vm, node, b[0], sizeof (*t)); - t->urpf = lb0->lb_urpf; - } b++; next++; n_left--; diff --git a/src/plugins/wireguard/wireguard_chachapoly.c b/src/plugins/wireguard/wireguard_chachapoly.c index 0dd7908d2e2..ad644ff6cb8 100644 --- a/src/plugins/wireguard/wireguard_chachapoly.c +++ b/src/plugins/wireguard/wireguard_chachapoly.c @@ -72,11 +72,11 @@ wg_xchacha20poly1305_encrypt (vlib_main_t *vm, u8 *src, u32 src_len, u8 *dst, u64 h_nonce; clib_memcpy (&h_nonce, nonce + 16, sizeof (h_nonce)); - h_nonce = le64toh (h_nonce); + h_nonce = clib_little_to_host_u64 (h_nonce); hchacha20 (derived_key, nonce, key); for (i = 0; i < (sizeof (derived_key) / sizeof (derived_key[0])); i++) - (derived_key[i]) = htole32 ((derived_key[i])); + (derived_key[i]) = clib_host_to_little_u32 ((derived_key[i])); uint32_t key_idx; @@ -102,11 +102,11 @@ wg_xchacha20poly1305_decrypt (vlib_main_t *vm, u8 *src, u32 src_len, u8 *dst, u64 h_nonce; clib_memcpy (&h_nonce, nonce + 16, sizeof (h_nonce)); - h_nonce = le64toh (h_nonce); + h_nonce = clib_little_to_host_u64 (h_nonce); hchacha20 (derived_key, nonce, key); for (i = 0; i < (sizeof (derived_key) / sizeof (derived_key[0])); i++) - (derived_key[i]) = htole32 ((derived_key[i])); + (derived_key[i]) = clib_host_to_little_u32 ((derived_key[i])); uint32_t key_idx; diff --git a/src/plugins/wireguard/wireguard_noise.c b/src/plugins/wireguard/wireguard_noise.c index 5fe2e44b03b..c3f28f442f5 100644 --- a/src/plugins/wireguard/wireguard_noise.c +++ b/src/plugins/wireguard/wireguard_noise.c @@ -751,8 +751,8 @@ noise_tai64n_now (uint8_t output[NOISE_TIMESTAMP_LEN]) unix_nanosec &= REJECT_INTERVAL_MASK; /* https://cr.yp.to/libtai/tai64.html */ - sec = htobe64 (0x400000000000000aULL + unix_sec); - nsec = htobe32 (unix_nanosec); + sec = clib_host_to_big_u64 (0x400000000000000aULL + unix_sec); + nsec = clib_host_to_big_u32 (unix_nanosec); /* memcpy to output buffer, assuming output could be unaligned. */ clib_memcpy (output, &sec, sizeof (sec)); diff --git a/src/tools/vppapigen/vppapigen_c.py b/src/tools/vppapigen/vppapigen_c.py index fb7de0a023f..c2e1e7da7b7 100755 --- a/src/tools/vppapigen/vppapigen_c.py +++ b/src/tools/vppapigen/vppapigen_c.py @@ -365,7 +365,7 @@ class FromJSON: write(" char *p = cJSON_GetStringValue(item);\n") write(" size_t plen = strlen(p);\n") write( - " {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen, {msgsize});\n".format( + " {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen);\n".format( msgvar=msgvar, msgsize=msgsize ) ) @@ -434,7 +434,7 @@ class FromJSON: cJSON *array = cJSON_GetObjectItem(o, "{n}"); int size = cJSON_GetArraySize(array); {lfield} = size; - {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size, {msgsize}); + {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size); {t} *d = (void *){realloc} + {msgsize}; {msgsize} += sizeof({t}) * size; for (i = 0; i < size; i++) {{ @@ -461,12 +461,12 @@ class FromJSON: write( " {realloc} = cJSON_realloc({realloc}, {msgsize} + " - "vec_len(s), {msgsize});\n".format( + "vec_len(s));\n".format( msgvar=msgvar, msgsize=msgsize, realloc=realloc ) ) write( - " memcpy((void *){realloc} + {msgsize}, s, " + " clib_memcpy((void *){realloc} + {msgsize}, s, " "vec_len(s));\n".format(realloc=realloc, msgsize=msgsize) ) write(" {msgsize} += vec_len(s);\n".format(msgsize=msgsize)) @@ -1143,20 +1143,14 @@ ENDIAN_STRINGS = { } -def get_endian_string(o, type): +def get_endian_string(o, fieldtype): """Return proper endian string conversion function""" - try: - if o.to_network: - return ENDIAN_STRINGS[type].replace("net_to_host", "host_to_net") - except: - pass - return ENDIAN_STRINGS[type] + return ENDIAN_STRINGS[fieldtype] def endianfun_array(o): """Generate endian functions for arrays""" forloop = """\ - {comment} ASSERT((u32){length} <= (u32)VL_API_MAX_ARRAY_SIZE); for (i = 0; i < {length}; i++) {{ a->{name}[i] = {format}(a->{name}[i]); @@ -1165,31 +1159,26 @@ def endianfun_array(o): forloop_format = """\ for (i = 0; i < {length}; i++) {{ - {type}_endian(&a->{name}[i]); + {type}_endian(&a->{name}[i], to_net); }} """ - to_network_comment = "" - try: - if o.to_network: - to_network_comment = """/* - * Array fields processed first to handle variable length arrays and size - * field endian conversion in the proper order for to-network messages. - * Message fields have been sorted by type in the code generator, thus fields - * in this generated code may be converted in a different order than specified - * in the *.api file. - */""" - except: - pass - output = "" if o.fieldtype == "u8" or o.fieldtype == "string" or o.fieldtype == "bool": output += " /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname) else: lfield = "a->" + o.lengthfield if o.lengthfield else o.length + if o.lengthfield: + output += ( + f" u32 count = to_net ? clib_host_to_net_u32(a->{o.lengthfield}) : " + f"a->{o.lengthfield};\n" + ) + lfield = "count" + else: + lfield = o.length + if o.fieldtype in ENDIAN_STRINGS: output += forloop.format( - comment=to_network_comment, length=lfield, format=get_endian_string(o, o.fieldtype), name=o.fieldname, @@ -1222,7 +1211,7 @@ def endianfun_obj(o): name=o.fieldname, format=get_endian_string(o, o.fieldtype) ) elif o.fieldtype.startswith("vl_api_"): - output += " {type}_endian(&a->{name});\n".format( + output += " {type}_endian(&a->{name}, to_net);\n".format( type=o.fieldtype, name=o.fieldname ) else: @@ -1254,19 +1243,12 @@ def endianfun(objs, modulename): output = output.format(module=modulename) signature = """\ -static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a) +static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a, bool to_net) {{ int i __attribute__((unused)); """ for t in objs: - # Outbound (to network) messages are identified by message nomenclature - # i.e. message names ending with these suffixes are 'to network' - if t.name.endswith("_reply") or t.name.endswith("_details"): - t.to_network = True - else: - t.to_network = False - if t.__class__.__name__ == "Enum" or t.__class__.__name__ == "EnumFlag": output += signature.format(name=t.name) if t.enumtype in ENDIAN_STRINGS: @@ -1300,15 +1282,7 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a) output += signature.format(name=t.name) - # For outbound (to network) messages: - # some arrays have dynamic length -- iterate over - # them before changing endianness for the length field - # by making the Array types show up first - if t.to_network: - t.block.sort(key=lambda x: x.type) - for o in t.block: - o.to_network = t.to_network output += endianfun_obj(o) output += "}\n\n" @@ -1852,7 +1826,7 @@ api_{n} (cJSON *o) }} mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC); - vl_api_{n}_t_endian(mp); + vl_api_{n}_t_endian(mp, 1); vac_write((char *)mp, len); cJSON_free(mp); @@ -1867,7 +1841,7 @@ api_{n} (cJSON *o) return 0; }} vl_api_{r}_t *rmp = (vl_api_{r}_t *)p; - vl_api_{r}_t_endian(rmp); + vl_api_{r}_t_endian(rmp, 0); return vl_api_{r}_t_tojson(rmp); }} @@ -1885,7 +1859,7 @@ api_{n} (cJSON *o) return 0; }} mp->_vl_msg_id = msg_id; - vl_api_{n}_t_endian(mp); + vl_api_{n}_t_endian(mp, 1); vac_write((char *)mp, len); cJSON_free(mp); @@ -1919,7 +1893,7 @@ api_{n} (cJSON *o) return 0; }} vl_api_{r}_t *rmp = (vl_api_{r}_t *)p; - vl_api_{r}_t_endian(rmp); + vl_api_{r}_t_endian(rmp, 0); cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp)); }} }} @@ -1941,7 +1915,7 @@ api_{n} (cJSON *o) }} mp->_vl_msg_id = msg_id; - vl_api_{n}_t_endian(mp); + vl_api_{n}_t_endian(mp, 1); vac_write((char *)mp, len); cJSON_free(mp); @@ -1962,14 +1936,14 @@ api_{n} (cJSON *o) u16 msg_id = ntohs(*((u16 *)p)); if (msg_id == reply_msg_id) {{ vl_api_{r}_t *rmp = (vl_api_{r}_t *)p; - vl_api_{r}_t_endian(rmp); + vl_api_{r}_t_endian(rmp, 0); cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp)); break; }} if (msg_id == details_msg_id) {{ vl_api_{d}_t *rmp = (vl_api_{d}_t *)p; - vl_api_{d}_t_endian(rmp); + vl_api_{d}_t_endian(rmp, 0); cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp)); }} }} diff --git a/src/vat2/vat2_helpers.h b/src/vat2/vat2_helpers.h index 7b197608a7b..d9ce2af6b35 100644 --- a/src/vat2/vat2_helpers.h +++ b/src/vat2/vat2_helpers.h @@ -29,7 +29,7 @@ vat2_control_ping (u32 context) vl_api_control_ping_t mp = {0}; mp._vl_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_CRC); mp.context = context; - vl_api_control_ping_t_endian(&mp); + vl_api_control_ping_t_endian (&mp, 1 /* to network */); vac_write((char *)&mp, sizeof(mp)); } diff --git a/src/vcl/vppcom.c b/src/vcl/vppcom.c index 58f6ac0a134..a557093e897 100644 --- a/src/vcl/vppcom.c +++ b/src/vcl/vppcom.c @@ -2087,7 +2087,16 @@ read_again: ASSERT (rv >= 0); if (peek) - return rv; + { + /* Request new notifications if more data enqueued */ + if (rv < n || rv == svm_fifo_max_dequeue_cons (rx_fifo)) + { + if (is_ct) + svm_fifo_unset_event (s->rx_fifo); + svm_fifo_unset_event (rx_fifo); + } + return rv; + } n_read += rv; diff --git a/src/vlib/CMakeLists.txt b/src/vlib/CMakeLists.txt index 7ec9b2050e9..3c354b764dd 100644 --- a/src/vlib/CMakeLists.txt +++ b/src/vlib/CMakeLists.txt @@ -55,6 +55,7 @@ install( # vlib shared library ############################################################################## +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") set(PLATFORM_SOURCES linux/pci.c linux/vfio.c @@ -64,6 +65,11 @@ set(PLATFORM_SOURCES set(PLATFORM_HEADERS linux/vfio.h ) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") +set(PLATFORM_SOURCES + freebsd/pci.c +) +endif() add_vpp_library(vlib SOURCES diff --git a/src/vlib/buffer.c b/src/vlib/buffer.c index 674f15d5dc6..5ba42270b1a 100644 --- a/src/vlib/buffer.c +++ b/src/vlib/buffer.c @@ -663,7 +663,7 @@ vlib_buffer_main_init_numa_alloc (struct vlib_main_t *vm, u32 numa_node, u8 unpriv) { vlib_buffer_main_t *bm = vm->buffer_main; - u32 buffers_per_numa = bm->buffers_per_numa; + u32 buffers_per_numa = bm->buffers_per_numa[numa_node]; clib_error_t *error; u32 buffer_size; uword n_pages, pagesize; @@ -680,6 +680,9 @@ vlib_buffer_main_init_numa_alloc (struct vlib_main_t *vm, u32 numa_node, "size (%llu)", buffer_size, pagesize); if (buffers_per_numa == 0) + buffers_per_numa = bm->default_buffers_per_numa; + + if (buffers_per_numa == 0) buffers_per_numa = unpriv ? VLIB_BUFFER_DEFAULT_BUFFERS_PER_NUMA_UNPRIV : VLIB_BUFFER_DEFAULT_BUFFERS_PER_NUMA; @@ -906,18 +909,48 @@ done: } static clib_error_t * +vlib_buffers_numa_configure (vlib_buffer_main_t *bm, u32 numa_node, + unformat_input_t *input) +{ + u32 buffers = 0; + + if (numa_node >= VLIB_BUFFER_MAX_NUMA_NODES) + return clib_error_return (0, "invalid numa node"); + + if (!input) + return 0; + + unformat_skip_white_space (input); + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "buffers %u", &buffers)) + ; + else + return unformat_parse_error (input); + } + + bm->buffers_per_numa[numa_node] = buffers; + return 0; +} + +static clib_error_t * vlib_buffers_configure (vlib_main_t * vm, unformat_input_t * input) { vlib_buffer_main_t *bm; + u32 numa_node; + unformat_input_t sub_input; + clib_error_t *error = 0; vlib_buffer_main_alloc (vm); bm = vm->buffer_main; bm->log2_page_size = CLIB_MEM_PAGE_SZ_UNKNOWN; + memset (bm->buffers_per_numa, 0, sizeof (bm->buffers_per_numa)); while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { - if (unformat (input, "buffers-per-numa %u", &bm->buffers_per_numa)) + if (unformat (input, "buffers-per-numa %u", + &bm->default_buffers_per_numa)) ; else if (unformat (input, "page-size %U", unformat_log2_page_size, &bm->log2_page_size)) @@ -925,6 +958,15 @@ vlib_buffers_configure (vlib_main_t * vm, unformat_input_t * input) else if (unformat (input, "default data-size %u", &bm->default_data_size)) ; + else if (unformat (input, "numa %u %U", &numa_node, + unformat_vlib_cli_sub_input, &sub_input)) + { + error = vlib_buffers_numa_configure (bm, numa_node, &sub_input); + unformat_free (&sub_input); + + if (error) + return error; + } else return unformat_parse_error (input); } diff --git a/src/vlib/buffer.h b/src/vlib/buffer.h index 7d45689ed19..aad9701080e 100644 --- a/src/vlib/buffer.h +++ b/src/vlib/buffer.h @@ -508,7 +508,8 @@ typedef struct u8 default_buffer_pool_index_for_numa[VLIB_BUFFER_MAX_NUMA_NODES]; /* config */ - u32 buffers_per_numa; + u32 default_buffers_per_numa; + u32 buffers_per_numa[VLIB_BUFFER_MAX_NUMA_NODES]; u16 ext_hdr_size; u32 default_data_size; clib_mem_page_sz_t log2_page_size; diff --git a/src/vlib/linux/pci.c b/src/vlib/linux/pci.c index f7c63bc3607..29ca3d97523 100644 --- a/src/vlib/linux/pci.c +++ b/src/vlib/linux/pci.c @@ -1561,14 +1561,17 @@ linux_pci_init (vlib_main_t * vm) ASSERT (sizeof (vlib_pci_addr_t) == sizeof (u32)); - addrs = vlib_pci_get_all_dev_addrs (); - vec_foreach (addr, addrs) + if (pm->pci_device_registrations) { - vlib_pci_device_info_t *d; - if ((d = vlib_pci_get_device_info (vm, addr, 0))) + addrs = vlib_pci_get_all_dev_addrs (); + vec_foreach (addr, addrs) { - init_device_from_registered (vm, d); - vlib_pci_free_device_info (d); + vlib_pci_device_info_t *d; + if ((d = vlib_pci_get_device_info (vm, addr, 0))) + { + init_device_from_registered (vm, d); + vlib_pci_free_device_info (d); + } } } diff --git a/src/vlib/pci/pci.c b/src/vlib/pci/pci.c index 0bc90c5532d..7284e6675fd 100644 --- a/src/vlib/pci/pci.c +++ b/src/vlib/pci/pci.c @@ -119,7 +119,7 @@ vlib_pci_intr_enable (vlib_main_t *vm, vlib_pci_dev_handle_t h) { const vlib_pci_config_reg_command_t cmd = { .intx_disable = 1 }; clib_error_t *err; - int already_set; + int already_set = 0; err = _vlib_pci_config_set_control_bit (vm, h, cmd.as_u16, 0, &already_set); log_debug (h, "interrupt%senabled", already_set ? " " : " already "); @@ -131,7 +131,7 @@ vlib_pci_intr_disable (vlib_main_t *vm, vlib_pci_dev_handle_t h) { const vlib_pci_config_reg_command_t cmd = { .intx_disable = 1 }; clib_error_t *err; - int already_set; + int already_set = 0; err = _vlib_pci_config_set_control_bit (vm, h, cmd.as_u16, 1, &already_set); log_debug (h, "interrupt%sdisabled", already_set ? " " : " already "); @@ -143,7 +143,7 @@ vlib_pci_bus_master_enable (vlib_main_t *vm, vlib_pci_dev_handle_t h) { const vlib_pci_config_reg_command_t cmd = { .bus_master = 1 }; clib_error_t *err; - int already_set; + int already_set = 0; err = _vlib_pci_config_set_control_bit (vm, h, cmd.as_u16, 1, &already_set); log_debug (h, "bus-master%senabled", already_set ? " " : " already "); @@ -155,7 +155,7 @@ vlib_pci_bus_master_disable (vlib_main_t *vm, vlib_pci_dev_handle_t h) { const vlib_pci_config_reg_command_t cmd = { .bus_master = 1 }; clib_error_t *err; - int already_set; + int already_set = 0; err = _vlib_pci_config_set_control_bit (vm, h, cmd.as_u16, 0, &already_set); log_debug (h, "bus-master%sdisabled", already_set ? " " : " already "); diff --git a/src/vlib/threads.c b/src/vlib/threads.c index bbcb4ec2979..ef2c5616f21 100644 --- a/src/vlib/threads.c +++ b/src/vlib/threads.c @@ -182,9 +182,7 @@ vlib_thread_init (vlib_main_t * vm) u32 n_vlib_mains = 1; u32 first_index = 1; u32 i; - pid_t pid; - uword *avail_cpu, *affinity_cpu; - uword n_cpus; + uword *avail_cpu; u32 stats_num_worker_threads_dir_index; stats_num_worker_threads_dir_index = @@ -195,41 +193,22 @@ vlib_thread_init (vlib_main_t * vm) tm->cpu_core_bitmap = os_get_online_cpu_core_bitmap (); tm->cpu_socket_bitmap = os_get_online_cpu_node_bitmap (); - /* get bitmap of active cpu cores vpp has affinity to */ - pid = getpid (); - tm->cpu_affinity_bitmap = os_get_cpu_affinity_bitmap (pid); - - /* if fetching affinity fails, return online cpu core bmp */ - if (tm->cpu_affinity_bitmap == 0) - tm->cpu_affinity_bitmap = os_get_online_cpu_core_bitmap (); - avail_cpu = clib_bitmap_dup (tm->cpu_core_bitmap); - affinity_cpu = clib_bitmap_dup (tm->cpu_affinity_bitmap); /* skip cores */ - n_cpus = clib_bitmap_count_set_bits (avail_cpu); - if (tm->skip_cores >= n_cpus) - return clib_error_return (0, "skip-core greater than available cpus"); - n_cpus = clib_bitmap_count_set_bits (affinity_cpu); - if (tm->skip_cores >= n_cpus) - return clib_error_return (0, "skip-core greater than affinity cpus"); - for (i = 0; i < tm->skip_cores; i++) { - uword c; - c = clib_bitmap_first_set (avail_cpu); + uword c = clib_bitmap_first_set (avail_cpu); if (c == ~0) return clib_error_return (0, "no available cpus to skip"); avail_cpu = clib_bitmap_set (avail_cpu, c, 0); - - c = clib_bitmap_first_set (affinity_cpu); - if (c == ~0) - return clib_error_return (0, "no available env cpus to skip"); - - affinity_cpu = clib_bitmap_set (affinity_cpu, c, 0); } + /* if main thread affinity is unspecified, set to current running cpu */ + if (tm->main_lcore == ~0) + tm->main_lcore = sched_getcpu (); + /* grab cpu for main thread */ if (tm->main_lcore != ~0) { @@ -237,17 +216,6 @@ vlib_thread_init (vlib_main_t * vm) return clib_error_return (0, "cpu %u is not available to be used" " for the main thread", tm->main_lcore); avail_cpu = clib_bitmap_set (avail_cpu, tm->main_lcore, 0); - affinity_cpu = clib_bitmap_set (affinity_cpu, tm->main_lcore, 0); - } - /* if auto enabled, grab first cpu vpp has affinity to for main thread */ - else if (tm->use_main_core_auto) - { - uword c = clib_bitmap_first_set (affinity_cpu); - if (c != ~0) - tm->main_lcore = c; - - avail_cpu = clib_bitmap_set (avail_cpu, tm->main_lcore, 0); - affinity_cpu = clib_bitmap_set (affinity_cpu, tm->main_lcore, 0); } /* assume that there is socket 0 only if there is no data from sysfs */ @@ -332,23 +300,13 @@ vlib_thread_init (vlib_main_t * vm) } else { - /* for automatic pinning, use cpu affinity list */ - uword n_env_cpu = 0; - n_env_cpu = clib_bitmap_count_set_bits (affinity_cpu); - - if (n_env_cpu < tr->count) - return clib_error_return (0, - "no available cpus to be used for" - " the '%s' thread #%u", - tr->name, n_env_cpu); - for (j = 0; j < tr->count; j++) { /* Do not use CPU 0 by default - leave it to the host and IRQs */ - uword avail_c0 = clib_bitmap_get (affinity_cpu, 0); - affinity_cpu = clib_bitmap_set (affinity_cpu, 0, 0); + uword avail_c0 = clib_bitmap_get (avail_cpu, 0); + avail_cpu = clib_bitmap_set (avail_cpu, 0, 0); - uword c = clib_bitmap_first_set (affinity_cpu); + uword c = clib_bitmap_first_set (avail_cpu); /* Use CPU 0 as a last resort */ if (c == ~0 && avail_c0) { @@ -362,15 +320,14 @@ vlib_thread_init (vlib_main_t * vm) " the '%s' thread #%u", tr->name, tr->count); - affinity_cpu = clib_bitmap_set (affinity_cpu, 0, avail_c0); - affinity_cpu = clib_bitmap_set (affinity_cpu, c, 0); + avail_cpu = clib_bitmap_set (avail_cpu, 0, avail_c0); + avail_cpu = clib_bitmap_set (avail_cpu, c, 0); tr->coremask = clib_bitmap_set (tr->coremask, c, 1); } } } clib_bitmap_free (avail_cpu); - clib_bitmap_free (affinity_cpu); tm->n_vlib_mains = n_vlib_mains; vlib_stats_set_gauge (stats_num_worker_threads_dir_index, n_vlib_mains - 1); @@ -417,6 +374,8 @@ void vlib_worker_thread_init (vlib_worker_thread_t * w) { vlib_thread_main_t *tm = vlib_get_thread_main (); + sigset_t signals; + int rv; /* * Note: disabling signals in worker threads as follows @@ -426,7 +385,17 @@ vlib_worker_thread_init (vlib_worker_thread_t * w) * sigfillset (&s); * pthread_sigmask (SIG_SETMASK, &s, 0); * } + * We can still disable signals for SIGINT,SIGHUP and SIGTERM as they don't + * trigger post-dump handlers anyway. */ + sigemptyset (&signals); + sigaddset (&signals, SIGINT); + sigaddset (&signals, SIGHUP); + sigaddset (&signals, SIGTERM); + rv = pthread_sigmask (SIG_BLOCK, &signals, NULL); + + if (rv) + clib_warning ("Failed to set the worker signal mask"); clib_mem_set_heap (w->thread_mheap); @@ -1169,6 +1138,7 @@ cpu_config (vlib_main_t * vm, unformat_input_t * input) u8 *name; uword *bitmap; u32 count; + int use_corelist = 0; tm->thread_registrations_by_name = hash_create_string (0, sizeof (uword)); @@ -1176,7 +1146,6 @@ cpu_config (vlib_main_t * vm, unformat_input_t * input) tm->sched_policy = ~0; tm->sched_priority = ~0; tm->main_lcore = ~0; - tm->use_main_core_auto = 0; tr = tm->next; @@ -1192,8 +1161,6 @@ cpu_config (vlib_main_t * vm, unformat_input_t * input) tm->use_pthreads = 1; else if (unformat (input, "thread-prefix %v", &tm->thread_prefix)) ; - else if (unformat (input, "main-core auto")) - tm->use_main_core_auto = 1; else if (unformat (input, "main-core %u", &tm->main_lcore)) ; else if (unformat (input, "skip-cores %u", &tm->skip_cores)) @@ -1223,6 +1190,7 @@ cpu_config (vlib_main_t * vm, unformat_input_t * input) tr->coremask = bitmap; tr->count = clib_bitmap_count_set_bits (tr->coremask); + use_corelist = 1; } else if (unformat @@ -1252,13 +1220,9 @@ cpu_config (vlib_main_t * vm, unformat_input_t * input) break; } - if (tm->main_lcore != ~0 && tm->use_main_core_auto) - { - return clib_error_return ( - 0, "cannot set both 'main-core %u' and 'main-core auto'", - tm->main_lcore); - } - + if (use_corelist && tm->main_lcore == ~0) + return clib_error_return (0, "main-core must be specified when using " + "corelist-* or coremask-* attribute"); if (tm->sched_priority != ~0) { if (tm->sched_policy == SCHED_FIFO || tm->sched_policy == SCHED_RR) diff --git a/src/vlib/threads.h b/src/vlib/threads.h index 3072d0e67dd..c671aa78c39 100644 --- a/src/vlib/threads.h +++ b/src/vlib/threads.h @@ -200,7 +200,7 @@ vlib_smp_unsafe_warning (void) } always_inline int -__foreach_vlib_main_helper (vlib_main_t *ii, vlib_main_t **p) +__foreach_vlib_main_helper (vlib_main_t *ii, vlib_main_t **p, int checks) { vlib_main_t *vm; u32 index = ii - (vlib_main_t *) 0; @@ -209,15 +209,17 @@ __foreach_vlib_main_helper (vlib_main_t *ii, vlib_main_t **p) return 0; *p = vm = vlib_global_main.vlib_mains[index]; - ASSERT (index == 0 || vm->parked_at_barrier == 1); + ASSERT (!checks || index == 0 || vm->parked_at_barrier == 1); return 1; } -#define foreach_vlib_main() \ +#define foreach_vlib_main__(checks) \ for (vlib_main_t *ii = 0, *this_vlib_main; \ - __foreach_vlib_main_helper (ii, &this_vlib_main); ii++) \ + __foreach_vlib_main_helper (ii, &this_vlib_main, checks); ii++) \ if (this_vlib_main) +#define foreach_vlib_main() foreach_vlib_main__ (1) + #define foreach_sched_policy_posix \ _ (SCHED_OTHER, OTHER, "other") \ _ (SCHED_FIFO, FIFO, "fifo") \ @@ -255,8 +257,6 @@ typedef struct int use_pthreads; - int use_main_core_auto; - /* Number of vlib_main / vnet_main clones */ u32 n_vlib_mains; @@ -284,9 +284,6 @@ typedef struct /* Bitmap of available CPU sockets (NUMA nodes) */ uword *cpu_socket_bitmap; - /* Bitmap of CPU affinity for VPP process */ - uword *cpu_affinity_bitmap; - /* Worker handoff queues */ vlib_frame_queue_main_t *frame_queue_mains; diff --git a/src/vlib/unix/main.c b/src/vlib/unix/main.c index ee28ca8f1aa..11d0cb1160c 100644 --- a/src/vlib/unix/main.c +++ b/src/vlib/unix/main.c @@ -40,6 +40,8 @@ #include <vlib/unix/unix.h> #include <vlib/unix/plugin.h> #include <vppinfra/unix.h> +#include <vppinfra/stack.h> +#include <vppinfra/format_ansi.h> #include <limits.h> #include <signal.h> @@ -97,20 +99,42 @@ int vlib_last_signum = 0; uword vlib_last_faulting_address = 0; static void +log_one_line () +{ + vec_terminate_c_string (syslog_msg); + if (unix_main.flags & (UNIX_FLAG_INTERACTIVE | UNIX_FLAG_NOSYSLOG)) + fprintf (stderr, "%s\n", syslog_msg); + else + syslog (LOG_ERR | LOG_DAEMON, "%s", syslog_msg); + vec_reset_length (syslog_msg); +} + +static void unix_signal_handler (int signum, siginfo_t * si, ucontext_t * uc) { uword fatal = 0; + int color = + (unix_main.flags & (UNIX_FLAG_INTERACTIVE | UNIX_FLAG_NOSYSLOG)) && + (unix_main.flags & UNIX_FLAG_NOCOLOR) == 0; /* These come in handy when looking at core files from optimized images */ vlib_last_signum = signum; vlib_last_faulting_address = (uword) si->si_addr; + if (color) + syslog_msg = format (syslog_msg, ANSI_FG_BR_RED); + syslog_msg = format (syslog_msg, "received signal %U, PC %U", format_signal, signum, format_ucontext_pc, uc); - if (signum == SIGSEGV) + if (signum == SIGSEGV || signum == SIGBUS) syslog_msg = format (syslog_msg, ", faulting address %p", si->si_addr); + if (color) + syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT); + + log_one_line (); + switch (signum) { /* these (caught) signals cause the application to exit */ @@ -120,11 +144,17 @@ unix_signal_handler (int signum, siginfo_t * si, ucontext_t * uc) */ if (unix_main.vlib_main && unix_main.vlib_main->main_loop_exit_set) { - syslog (LOG_ERR | LOG_DAEMON, "received SIGTERM, exiting..."); + syslog_msg = format ( + syslog_msg, "received SIGTERM from PID %d UID %d, exiting...", + si->si_pid, si->si_uid); + log_one_line (); unix_main.vlib_main->main_loop_exit_now = 1; } else - syslog (LOG_ERR | LOG_DAEMON, "IGNORE early SIGTERM..."); + { + syslog_msg = format (syslog_msg, "IGNORE early SIGTERM..."); + log_one_line (); + } break; /* fall through */ case SIGQUIT: @@ -144,26 +174,75 @@ unix_signal_handler (int signum, siginfo_t * si, ucontext_t * uc) break; } - /* Null terminate. */ - vec_add1 (syslog_msg, 0); if (fatal) { - syslog (LOG_ERR | LOG_DAEMON, "%s", syslog_msg); + int skip = 1, index = 0; - /* Address of callers: outer first, inner last. */ - uword callers[15]; - uword n_callers = clib_backtrace (callers, ARRAY_LEN (callers), 0); - int i; - for (i = 0; i < n_callers; i++) + foreach_clib_stack_frame (sf) { - vec_reset_length (syslog_msg); + if (sf->is_signal_frame) + { + int pipefd[2]; + const int n_bytes = 20; + u8 *ip = (void *) sf->ip; + + if (pipe (pipefd) == 0) + { + /* check PC points to valid memory */ + if (write (pipefd[1], ip, n_bytes) == n_bytes) + { + syslog_msg = format (syslog_msg, "Code: "); + if (color) + syslog_msg = format (syslog_msg, ANSI_FG_CYAN); + for (int i = 0; i < n_bytes; i++) + syslog_msg = format (syslog_msg, " %02x", ip[i]); + if (color) + syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT); + } + else + { + syslog_msg = format ( + syslog_msg, "PC contains invalid memory address"); + } + log_one_line (); + foreach_int (i, 0, 1) + close (pipefd[i]); + } + skip = 0; + } + + if (skip) + continue; + + syslog_msg = format (syslog_msg, "#%-2d ", index++); + if (color) + syslog_msg = format (syslog_msg, ANSI_FG_BLUE); + syslog_msg = format (syslog_msg, "0x%016lx", sf->ip); + if (color) + syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT); + + if (sf->name[0]) + { + if (color) + syslog_msg = format (syslog_msg, ANSI_FG_YELLOW); + syslog_msg = + format (syslog_msg, " %s + 0x%x", sf->name, sf->offset); + if (color) + syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT); + } - syslog_msg = - format (syslog_msg, "#%-2d 0x%016lx %U%c", i, callers[i], - format_clib_elf_symbol_with_address, callers[i], 0); + log_one_line (); - syslog (LOG_ERR | LOG_DAEMON, "%s", syslog_msg); + if (sf->file_name) + { + if (color) + syslog_msg = format (syslog_msg, ANSI_FG_GREEN); + syslog_msg = format (syslog_msg, " from %s", sf->file_name); + if (color) + syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT); + log_one_line (); + } } /* have to remove SIGABRT to avoid recursive - os_exit calling abort() */ @@ -175,9 +254,6 @@ unix_signal_handler (int signum, siginfo_t * si, ucontext_t * uc) else os_exit (1); } - else - clib_warning ("%s", syslog_msg); - } static clib_error_t * diff --git a/src/vlib/unix/plugin.c b/src/vlib/unix/plugin.c index fd3a050b944..77e4633e14a 100644 --- a/src/vlib/unix/plugin.c +++ b/src/vlib/unix/plugin.c @@ -88,19 +88,14 @@ extract (u8 * sp, u8 * ep) */ static clib_error_t * -r2_to_reg (elf_main_t * em, vlib_plugin_r2_t * r2, - vlib_plugin_registration_t * reg) +r2_to_reg (elf_main_t *em, vlib_plugin_r2_t *r2, + vlib_plugin_registration_t *reg, elf_section_t *data_section) { - clib_error_t *error; - elf_section_t *section; uword data_segment_offset; u8 *data; /* It turns out that the strings land in the ".data" section */ - error = elf_get_section_by_name (em, ".data", §ion); - if (error) - return error; - data = elf_get_section_contents (em, section->index, 1); + data = elf_get_section_contents (em, data_section->index, 1); /* * Offsets in the ".vlib_plugin_r2" section @@ -177,13 +172,52 @@ load_one_plugin (plugin_main_t * pm, plugin_info_t * pi, int from_early_init) error = elf_get_section_by_name (&em, ".vlib_plugin_r2", §ion); if (error == 0) { + elf_section_t *data_section; + elf_relocation_table_t *rt; + elf_relocation_with_addend_t *r; + elf_symbol_table_t *st; + elf64_symbol_t *sym, *symok = 0; + data = elf_get_section_contents (&em, section->index, 1); r2 = (vlib_plugin_r2_t *) data; + + elf_get_section_by_name (&em, ".data", &data_section); + + // Find first symbol in .vlib_plugin_r2 section. + vec_foreach (st, em.symbol_tables) + { + vec_foreach (sym, st->symbols) + { + if (sym->section_index == section->index) + { + symok = sym; + break; + } + } + } + + // Relocate section data as per relocation tables. + if (symok != 0) + { + vec_foreach (rt, em.relocation_tables) + { + vec_foreach (r, rt->relocations) + { + if (r->address >= symok->value && + r->address < symok->value + symok->size) + { + *(uword *) ((void *) data + r->address - symok->value) += + r->addend - data_section->header.exec_address; + } + } + } + } + reg = clib_mem_alloc (sizeof (*reg)); memset (reg, 0, sizeof (*reg)); reg->default_disabled = r2->default_disabled != 0; - error = r2_to_reg (&em, r2, reg); + error = r2_to_reg (&em, r2, reg, data_section); if (error) { PLUGIN_LOG_ERR ("Bad r2 registration: %s\n", (char *) pi->name); @@ -306,8 +340,12 @@ process_reg: } vec_free (version_required); +#if defined(RTLD_DEEPBIND) handle = dlopen ((char *) pi->filename, RTLD_LAZY | (reg->deep_bind ? RTLD_DEEPBIND : 0)); +#else + handle = dlopen ((char *) pi->filename, RTLD_LAZY); +#endif if (handle == 0) { diff --git a/src/vlibapi/api_common.h b/src/vlibapi/api_common.h index 62a8d4c62d8..c09334136c0 100644 --- a/src/vlibapi/api_common.h +++ b/src/vlibapi/api_common.h @@ -235,8 +235,8 @@ typedef struct /** Message convert function vector */ void *(*fromjson_handler) (cJSON *, int *); - /** Message endian handler vector */ - void (*endian_handler) (void *); + /** Message endian handler vector. */ + void (*endian_handler) (void *, bool to_net); /** Message calc size function vector */ uword (*calc_size_func) (void *); diff --git a/src/vlibapi/api_helper_macros.h b/src/vlibapi/api_helper_macros.h index 9c93d33934b..0380692b80e 100644 --- a/src/vlibapi/api_helper_macros.h +++ b/src/vlibapi/api_helper_macros.h @@ -29,9 +29,9 @@ #define _NATIVE_TO_NETWORK(t, rmp) \ api_main_t *am = vlibapi_get_main (); \ - void (*endian_fp) (void *); \ + void (*endian_fp) (void *, bool); \ endian_fp = am->msg_data[t + (REPLY_MSG_ID_BASE)].endian_handler; \ - (*endian_fp) (rmp); + (*endian_fp) (rmp, 1 /* to network */); #define REPLY_MACRO(msg) \ do \ diff --git a/src/vlibapi/api_shared.c b/src/vlibapi/api_shared.c index 7de1906f17a..79064b292c9 100644 --- a/src/vlibapi/api_shared.c +++ b/src/vlibapi/api_shared.c @@ -230,7 +230,7 @@ vl_msg_api_trace_write_one (api_main_t *am, u8 *msg, FILE *fp) if (m && m->endian_handler) { - m->endian_handler (tmpmem); + m->endian_handler (tmpmem, 1); } if (m && m->tojson_handler) @@ -561,7 +561,7 @@ msg_handler_internal (api_main_t *am, void *the_msg, uword msg_len, } if (m->is_autoendian) - m->endian_handler (the_msg); + m->endian_handler (the_msg, 0); if (PREDICT_FALSE (vec_len (am->perf_counter_cbs) != 0)) clib_call_callbacks (am->perf_counter_cbs, am, id, diff --git a/src/vlibmemory/memclnt_api.c b/src/vlibmemory/memclnt_api.c index d4106b10559..b0b0c72eae0 100644 --- a/src/vlibmemory/memclnt_api.c +++ b/src/vlibmemory/memclnt_api.c @@ -197,6 +197,7 @@ vlib_api_init (void) cJSON_Hooks cjson_hooks = { .malloc_fn = clib_mem_alloc, .free_fn = clib_mem_free, + .realloc_fn = clib_mem_realloc, }; cJSON_InitHooks (&cjson_hooks); diff --git a/src/vlibmemory/memory_api.c b/src/vlibmemory/memory_api.c index 39c6b0fd15b..57373b91e31 100644 --- a/src/vlibmemory/memory_api.c +++ b/src/vlibmemory/memory_api.c @@ -823,9 +823,9 @@ vl_mem_api_handler_with_vm_node (api_main_t *am, svm_region_t *vlib_rp, if (m->is_autoendian) { - void (*endian_fp) (void *); + void (*endian_fp) (void *, bool); endian_fp = am->msg_data[id].endian_handler; - (*endian_fp) (the_msg); + (*endian_fp) (the_msg, 0); } if (PREDICT_FALSE (vec_len (am->perf_counter_cbs) != 0)) clib_call_callbacks (am->perf_counter_cbs, am, id, 0 /* before */); diff --git a/src/vlibmemory/vlib_api_cli.c b/src/vlibmemory/vlib_api_cli.c index 4492f5af980..6ae81cd13df 100644 --- a/src/vlibmemory/vlib_api_cli.c +++ b/src/vlibmemory/vlib_api_cli.c @@ -554,7 +554,7 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename, } if (m) { - m->endian_handler (tmpbuf + sizeof (uword)); + m->endian_handler (tmpbuf + sizeof (uword), 1 /* to network */); } } @@ -674,7 +674,7 @@ vl_msg_print_trace (u8 *msg, void *ctx) clib_memcpy_fast (tmpbuf, msg, msg_length); msg = tmpbuf; - m->endian_handler (tmpbuf); + m->endian_handler (tmpbuf, 0 /* from network */); } vlib_cli_output (a->vm, "%U\n", @@ -824,7 +824,7 @@ vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o) } if (clib_arch_is_little_endian) - m->endian_handler (msg); + m->endian_handler (msg, 1 /* to network */); if (!m->handler) { diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt index fb8d294009d..0554e2f0f60 100644 --- a/src/vnet/CMakeLists.txt +++ b/src/vnet/CMakeLists.txt @@ -94,6 +94,7 @@ list(APPEND VNET_HEADERS format_fns.h ip/ip_format_fns.h ip/ip_sas.h + ip/vtep.h ethernet/ethernet_format_fns.h ethernet/ethernet_types_api.h ) @@ -813,24 +814,6 @@ list(APPEND VNET_API_FILES ) ############################################################################## -# mpls segment routing -############################################################################## - -list(APPEND VNET_SOURCES - srmpls/sr_mpls_policy.c - srmpls/sr_mpls_steering.c - srmpls/sr_mpls_api.c -) - -list(APPEND VNET_HEADERS - srmpls/sr_mpls.h -) - -list(APPEND VNET_API_FILES - srmpls/sr_mpls.api -) - -############################################################################## # IPFIX / netflow v10 ############################################################################## list(APPEND VNET_SOURCES @@ -1162,6 +1145,7 @@ list(APPEND VNET_HEADERS fib/fib_path_list.h fib/fib_sas.h fib/fib_source.h + fib/fib_urpf_list.h ) list(APPEND VNET_API_FILES @@ -1409,6 +1393,7 @@ list (APPEND VNET_SOURCES list(APPEND VNET_HEADERS arp/arp.h + arp/arp_packet.h ) list(APPEND VNET_API_FILES arp/arp.api) @@ -1460,7 +1445,6 @@ add_vat_test_library(vnet ip/ip_test.c arp/arp_test.c ip6-nd/ip6_nd_test.c - srmpls/sr_mpls_test.c session/session_test.c l2/l2_test.c ipsec/ipsec_test.c diff --git a/src/vnet/dev/counters.c b/src/vnet/dev/counters.c index 0a1e0a7419d..d02839d664f 100644 --- a/src/vnet/dev/counters.c +++ b/src/vnet/dev/counters.c @@ -54,7 +54,7 @@ vnet_dev_counters_clear (vlib_main_t *vm, vnet_dev_counter_main_t *cm) { for (int i = 0; i < cm->n_counters; i++) { - cm->counter_start[i] = cm->counter_data[i]; + cm->counter_start[i] += cm->counter_data[i]; cm->counter_data[i] = 0; } } diff --git a/src/vnet/dev/dev.h b/src/vnet/dev/dev.h index bbf2f9dff21..eb06eeba34e 100644 --- a/src/vnet/dev/dev.h +++ b/src/vnet/dev/dev.h @@ -115,6 +115,7 @@ typedef struct vnet_dev_rx_queue_op_t *start; vnet_dev_rx_queue_op_no_rv_t *stop; vnet_dev_rx_queue_op_no_rv_t *free; + vnet_dev_rx_queue_op_no_rv_t *clear_counters; format_function_t *format_info; } vnet_dev_rx_queue_ops_t; @@ -124,6 +125,7 @@ typedef struct vnet_dev_tx_queue_op_t *start; vnet_dev_tx_queue_op_no_rv_t *stop; vnet_dev_tx_queue_op_no_rv_t *free; + vnet_dev_tx_queue_op_no_rv_t *clear_counters; format_function_t *format_info; } vnet_dev_tx_queue_ops_t; @@ -245,6 +247,7 @@ typedef struct vnet_dev_port_op_no_rv_t *stop; vnet_dev_port_op_no_rv_t *deinit; vnet_dev_port_op_no_rv_t *free; + vnet_dev_port_op_no_rv_t *clear_counters; format_function_t *format_status; format_function_t *format_flow; } vnet_dev_port_ops_t; diff --git a/src/vnet/dev/format.c b/src/vnet/dev/format.c index ed83a0eba95..f599c0f8b85 100644 --- a/src/vnet/dev/format.c +++ b/src/vnet/dev/format.c @@ -101,7 +101,7 @@ format_vnet_dev_port_info (u8 *s, va_list *args) u32 indent = format_get_indent (s); s = format (s, "Hardware Address is %U", format_vnet_dev_hw_addr, - &port->attr.hw_addr); + &port->primary_hw_addr); s = format (s, ", %u RX queues (max %u), %u TX queues (max %u)", pool_elts (port->rx_queues), port->attr.max_rx_queues, pool_elts (port->tx_queues), port->attr.max_tx_queues); diff --git a/src/vnet/dev/port.c b/src/vnet/dev/port.c index 8a6df54cbc8..5b4b8cdc7b8 100644 --- a/src/vnet/dev/port.c +++ b/src/vnet/dev/port.c @@ -733,16 +733,26 @@ vnet_dev_port_if_remove (vlib_main_t *vm, vnet_dev_port_t *port) void vnet_dev_port_clear_counters (vlib_main_t *vm, vnet_dev_port_t *port) { - if (port->counter_main) + if (port->port_ops.clear_counters) + port->port_ops.clear_counters (vm, port); + else if (port->counter_main) vnet_dev_counters_clear (vm, port->counter_main); foreach_vnet_dev_port_rx_queue (q, port) - if (q->counter_main) - vnet_dev_counters_clear (vm, q->counter_main); + { + if (port->rx_queue_ops.clear_counters) + port->rx_queue_ops.clear_counters (vm, q); + else if (q->counter_main) + vnet_dev_counters_clear (vm, q->counter_main); + } foreach_vnet_dev_port_tx_queue (q, port) - if (q->counter_main) - vnet_dev_counters_clear (vm, q->counter_main); + { + if (port->tx_queue_ops.clear_counters) + port->tx_queue_ops.clear_counters (vm, q); + else if (q->counter_main) + vnet_dev_counters_clear (vm, q->counter_main); + } log_notice (port->dev, "counters cleared on port %u", port->port_id); } diff --git a/src/vnet/devices/virtio/node.c b/src/vnet/devices/virtio/node.c index 8c837575cf8..027e1ed4e74 100644 --- a/src/vnet/devices/virtio/node.c +++ b/src/vnet/devices/virtio/node.c @@ -282,6 +282,16 @@ virtio_device_input_gso_inline (vlib_main_t *vm, vlib_node_runtime_t *node, if (n_left == 0) return 0; + if (PREDICT_FALSE (n_left == vring->queue_size)) + { + /* + * Informational error logging when VPP is not pulling packets fast + * enough. + */ + vlib_error_count (vm, node->node_index, VIRTIO_INPUT_ERROR_FULL_RX_QUEUE, + 1); + } + if (type == VIRTIO_IF_TYPE_TUN) { next_index = VNET_DEVICE_INPUT_NEXT_IP4_INPUT; diff --git a/src/vnet/devices/virtio/virtio_inline.h b/src/vnet/devices/virtio/virtio_inline.h index 179f319aa4c..41bba755934 100644 --- a/src/vnet/devices/virtio/virtio_inline.h +++ b/src/vnet/devices/virtio/virtio_inline.h @@ -17,6 +17,7 @@ #define foreach_virtio_input_error \ _ (BUFFER_ALLOC, "buffer alloc error") \ + _ (FULL_RX_QUEUE, "full rx queue (driver tx drop)") \ _ (UNKNOWN, "unknown") typedef enum diff --git a/src/vnet/ethernet/node.c b/src/vnet/ethernet/node.c index e2558eeca41..03cbdde1c2b 100644 --- a/src/vnet/ethernet/node.c +++ b/src/vnet/ethernet/node.c @@ -982,8 +982,31 @@ eth_input_process_frame (vlib_main_t * vm, vlib_node_runtime_t * node, else { for (int j = 0; j < 16; j++) - if (next[j] == 0) - slowpath_indices[n_slowpath++] = i + j; + { + if (next[j] == 0) + slowpath_indices[n_slowpath++] = i + j; + else if (dmac_check && main_is_l3 && dmacs_bad[i + j]) + { + next[j] = 0; + slowpath_indices[n_slowpath++] = i + j; + } + } + } + } + else + { + if (dmac_check && main_is_l3) + { + u8x16 dmac_bad = u8x16_load_unaligned (&dmacs_bad[i]); + if (!u8x16_is_all_zero (dmac_bad)) + { + for (int j = 0; j < 16; j++) + if (dmacs_bad[i + j]) + { + next[j] = 0; + slowpath_indices[n_slowpath++] = i + j; + } + } } } @@ -994,7 +1017,12 @@ eth_input_process_frame (vlib_main_t * vm, vlib_node_runtime_t * node, continue; } #endif - if (main_is_l3 && etype[0] == et_ip4) + if (dmac_check && main_is_l3 && dmacs_bad[i]) + { + next[0] = 0; + slowpath_indices[n_slowpath++] = i; + } + else if (main_is_l3 && etype[0] == et_ip4) next[0] = next_ip4; else if (main_is_l3 && etype[0] == et_ip6) next[0] = next_ip6; @@ -1052,7 +1080,7 @@ eth_input_process_frame (vlib_main_t * vm, vlib_node_runtime_t * node, } else { - /* untagged packet with not well known etyertype */ + /* untagged packet with not well known ethertype */ if (last_unknown_etype != etype) { last_unknown_etype = etype; diff --git a/src/vnet/fib/fib_api.c b/src/vnet/fib/fib_api.c index 07d6699d87a..1b1c0d113c0 100644 --- a/src/vnet/fib/fib_api.c +++ b/src/vnet/fib/fib_api.c @@ -190,6 +190,7 @@ fib_api_path_decode (vl_api_fib_path_t *in, break; case FIB_API_PATH_TYPE_DROP: out->frp_flags |= FIB_ROUTE_PATH_DROP; + out->frp_sw_if_index = ntohl(in->sw_if_index); break; case FIB_API_PATH_TYPE_LOCAL: out->frp_flags |= FIB_ROUTE_PATH_LOCAL; diff --git a/src/vnet/fib/fib_types.c b/src/vnet/fib/fib_types.c index c4472c7122d..9abb89bc6a0 100644 --- a/src/vnet/fib/fib_types.c +++ b/src/vnet/fib/fib_types.c @@ -715,6 +715,10 @@ unformat_fib_route_path (unformat_input_t * input, va_list * args) rpath->frp_proto = DPO_PROTO_IP6; rpath->frp_flags = FIB_ROUTE_PATH_INTF_RX; } + else if (unformat (input, "drop")) + { + rpath->frp_flags = FIB_ROUTE_PATH_DROP; + } else if (unformat (input, "local")) { clib_memset (&rpath->frp_addr, 0, sizeof (rpath->frp_addr)); diff --git a/src/vnet/gso/gso.h b/src/vnet/gso/gso.h index 883a4941ee2..dee5da5c70b 100644 --- a/src/vnet/gso/gso.h +++ b/src/vnet/gso/gso.h @@ -39,13 +39,13 @@ gso_init_bufs_from_template_base (vlib_buffer_t **bufs, vlib_buffer_t *b0, u32 flags, u16 n_bufs, u16 hdr_sz) { u32 i = n_bufs; - while (i >= 4) + while (i >= 6) { /* prefetches */ CLIB_PREFETCH (bufs[2], 2 * CLIB_CACHE_LINE_BYTES, LOAD); CLIB_PREFETCH (bufs[3], 2 * CLIB_CACHE_LINE_BYTES, LOAD); - vlib_prefetch_buffer_data (bufs[2], LOAD); - vlib_prefetch_buffer_data (bufs[3], LOAD); + vlib_prefetch_buffer_data (bufs[4], LOAD); + vlib_prefetch_buffer_data (bufs[5], LOAD); /* copying objects from cacheline 0 */ bufs[0]->current_data = 0; @@ -70,10 +70,26 @@ gso_init_bufs_from_template_base (vlib_buffer_t **bufs, vlib_buffer_t *b0, bufs[0]->total_length_not_including_first_buffer = 0; bufs[1]->total_length_not_including_first_buffer = 0; + clib_memcpy_fast (&bufs[0]->opaque2, &b0->opaque2, sizeof (b0->opaque2)); + clib_memcpy_fast (&bufs[1]->opaque2, &b0->opaque2, sizeof (b0->opaque2)); + /* copying data */ clib_memcpy_fast (bufs[0]->data, vlib_buffer_get_current (b0), hdr_sz); clib_memcpy_fast (bufs[1]->data, vlib_buffer_get_current (b0), hdr_sz); + /* header offset fixup */ + vnet_buffer (bufs[0])->l2_hdr_offset -= b0->current_data; + vnet_buffer (bufs[0])->l3_hdr_offset -= b0->current_data; + vnet_buffer (bufs[0])->l4_hdr_offset -= b0->current_data; + vnet_buffer2 (bufs[0])->outer_l3_hdr_offset -= b0->current_data; + vnet_buffer2 (bufs[0])->outer_l4_hdr_offset -= b0->current_data; + + vnet_buffer (bufs[1])->l2_hdr_offset -= b0->current_data; + vnet_buffer (bufs[1])->l3_hdr_offset -= b0->current_data; + vnet_buffer (bufs[1])->l4_hdr_offset -= b0->current_data; + vnet_buffer2 (bufs[1])->outer_l3_hdr_offset -= b0->current_data; + vnet_buffer2 (bufs[1])->outer_l4_hdr_offset -= b0->current_data; + bufs += 2; i -= 2; } @@ -92,10 +108,18 @@ gso_init_bufs_from_template_base (vlib_buffer_t **bufs, vlib_buffer_t *b0, /* copying objects from cacheline 1 */ bufs[0]->trace_handle = b0->trace_handle; bufs[0]->total_length_not_including_first_buffer = 0; + clib_memcpy_fast (&bufs[0]->opaque2, &b0->opaque2, sizeof (b0->opaque2)); /* copying data */ clib_memcpy_fast (bufs[0]->data, vlib_buffer_get_current (b0), hdr_sz); + /* header offset fixup */ + vnet_buffer (bufs[0])->l2_hdr_offset -= b0->current_data; + vnet_buffer (bufs[0])->l3_hdr_offset -= b0->current_data; + vnet_buffer (bufs[0])->l4_hdr_offset -= b0->current_data; + vnet_buffer2 (bufs[0])->outer_l3_hdr_offset -= b0->current_data; + vnet_buffer2 (bufs[0])->outer_l4_hdr_offset -= b0->current_data; + bufs++; i--; } @@ -103,28 +127,41 @@ gso_init_bufs_from_template_base (vlib_buffer_t **bufs, vlib_buffer_t *b0, static_always_inline void gso_fixup_segmented_buf (vlib_main_t *vm, vlib_buffer_t *b0, u32 next_tcp_seq, - int is_l2, int is_ip6, generic_header_offset_t *gho, - clib_ip_csum_t *c, u8 tcp_flags) + int is_l2, u8 oflags, u16 hdr_sz, u16 l4_hdr_sz, + clib_ip_csum_t *c, u8 tcp_flags, u8 is_prefetch, + vlib_buffer_t *b1) { - ip4_header_t *ip4 = - (ip4_header_t *) (vlib_buffer_get_current (b0) + gho->l3_hdr_offset + - gho->outer_hdr_sz); - ip6_header_t *ip6 = - (ip6_header_t *) (vlib_buffer_get_current (b0) + gho->l3_hdr_offset + - gho->outer_hdr_sz); - tcp_header_t *tcp = - (tcp_header_t *) (vlib_buffer_get_current (b0) + gho->l4_hdr_offset + - gho->outer_hdr_sz); + i16 l3_hdr_offset = vnet_buffer (b0)->l3_hdr_offset; + i16 l4_hdr_offset = vnet_buffer (b0)->l4_hdr_offset; + + ip4_header_t *ip4 = (ip4_header_t *) (b0->data + l3_hdr_offset); + ip6_header_t *ip6 = (ip6_header_t *) (b0->data + l3_hdr_offset); + tcp_header_t *tcp = (tcp_header_t *) (b0->data + l4_hdr_offset); tcp->flags = tcp_flags; tcp->seq_number = clib_host_to_net_u32 (next_tcp_seq); c->odd = 0; - if (is_ip6) + if (oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM) + { + ip4->length = + clib_host_to_net_u16 (b0->current_length - hdr_sz + + (l4_hdr_offset - l3_hdr_offset) + l4_hdr_sz); + ip4->checksum = 0; + ip4->checksum = ip4_header_checksum (ip4); + vnet_buffer_offload_flags_clear (b0, (VNET_BUFFER_OFFLOAD_F_IP_CKSUM | + VNET_BUFFER_OFFLOAD_F_TCP_CKSUM)); + c->sum += clib_mem_unaligned (&ip4->src_address, u32); + c->sum += clib_mem_unaligned (&ip4->dst_address, u32); + c->sum += clib_host_to_net_u32 ( + (clib_net_to_host_u16 (ip4->length) - ip4_header_bytes (ip4)) + + (ip4->protocol << 16)); + } + else { - ip6->payload_length = clib_host_to_net_u16 ( - b0->current_length - gho->l4_hdr_offset - gho->outer_hdr_sz); + ip6->payload_length = + clib_host_to_net_u16 (b0->current_length - hdr_sz + l4_hdr_sz); vnet_buffer_offload_flags_clear (b0, VNET_BUFFER_OFFLOAD_F_TCP_CKSUM); ip6_psh_t psh = { 0 }; u32 *p = (u32 *) &psh; @@ -135,24 +172,15 @@ gso_fixup_segmented_buf (vlib_main_t *vm, vlib_buffer_t *b0, u32 next_tcp_seq, for (int i = 0; i < 10; i++) c->sum += p[i]; } - else - { - ip4->length = clib_host_to_net_u16 ( - b0->current_length - gho->l3_hdr_offset - gho->outer_hdr_sz); - if (gho->gho_flags & GHO_F_IP4) - ip4->checksum = ip4_header_checksum (ip4); - vnet_buffer_offload_flags_clear (b0, (VNET_BUFFER_OFFLOAD_F_IP_CKSUM | - VNET_BUFFER_OFFLOAD_F_TCP_CKSUM)); - c->sum += clib_mem_unaligned (&ip4->src_address, u32); - c->sum += clib_mem_unaligned (&ip4->dst_address, u32); - c->sum += clib_host_to_net_u32 ( - (clib_net_to_host_u16 (ip4->length) - ip4_header_bytes (ip4)) + - (ip4->protocol << 16)); - } - clib_ip_csum_chunk (c, (u8 *) tcp, gho->l4_hdr_sz); + + if (is_prefetch) + CLIB_PREFETCH (vlib_buffer_get_current (b1) + hdr_sz, + CLIB_CACHE_LINE_BYTES, LOAD); + + clib_ip_csum_chunk (c, (u8 *) tcp, l4_hdr_sz); tcp->checksum = clib_ip_csum_fold (c); - if (!is_l2 && ((gho->gho_flags & GHO_F_TUNNEL) == 0)) + if (!is_l2 && ((oflags & VNET_BUFFER_OFFLOAD_F_TNL_MASK) == 0)) { u32 adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; @@ -169,16 +197,20 @@ gso_fixup_segmented_buf (vlib_main_t *vm, vlib_buffer_t *b0, u32 next_tcp_seq, static_always_inline u32 gso_segment_buffer_inline (vlib_main_t *vm, vnet_interface_per_thread_data_t *ptd, - vlib_buffer_t *b, generic_header_offset_t *gho, - int is_l2, int is_ip6) + vlib_buffer_t *b, int is_l2) { vlib_buffer_t **bufs = 0; u32 n_tx_bytes = 0; + + u8 oflags = vnet_buffer (b)->oflags; + i16 l4_hdr_offset = vnet_buffer (b)->l4_hdr_offset; u16 gso_size = vnet_buffer2 (b)->gso_size; + u16 l4_hdr_sz = vnet_buffer2 (b)->gso_l4_hdr_sz; + u8 tcp_flags = 0, tcp_flags_no_fin_psh = 0; u32 default_bflags = b->flags & ~(VNET_BUFFER_F_GSO | VLIB_BUFFER_NEXT_PRESENT); - u16 hdr_sz = gho->hdr_sz + gho->outer_hdr_sz; + u16 hdr_sz = (l4_hdr_offset - b->current_data) + l4_hdr_sz; u32 next_tcp_seq = 0, tcp_seq = 0; u32 data_size = vlib_buffer_length_in_chain (vm, b) - hdr_sz; u16 size = @@ -200,9 +232,8 @@ gso_segment_buffer_inline (vlib_main_t *vm, vec_validate (bufs, n_bufs - 1); vlib_get_buffers (vm, ptd->split_buffers, bufs, n_bufs); - tcp_header_t *tcp = - (tcp_header_t *) (vlib_buffer_get_current (b) + gho->l4_hdr_offset + - gho->outer_hdr_sz); + tcp_header_t *tcp = (tcp_header_t *) (b->data + l4_hdr_offset); + tcp_seq = next_tcp_seq = clib_net_to_host_u32 (tcp->seq_number); /* store original flags for last packet and reset FIN and PSH */ tcp_flags = tcp->flags; @@ -247,11 +278,11 @@ gso_segment_buffer_inline (vlib_main_t *vm, if (0 == dst_left && data_size) { vlib_prefetch_buffer_header (bufs[i + 1], LOAD); - vlib_prefetch_buffer_data (bufs[i + 1], LOAD); n_tx_bytes += bufs[i]->current_length; - gso_fixup_segmented_buf (vm, bufs[i], tcp_seq, is_l2, is_ip6, gho, - &c, tcp_flags_no_fin_psh); + gso_fixup_segmented_buf (vm, bufs[i], tcp_seq, is_l2, oflags, hdr_sz, + l4_hdr_sz, &c, tcp_flags_no_fin_psh, 1, + bufs[i + 1]); i++; dst_left = size; dst_ptr = vlib_buffer_get_current (bufs[i]) + hdr_sz; @@ -264,8 +295,8 @@ gso_segment_buffer_inline (vlib_main_t *vm, ASSERT ((i + 1) == n_alloc); n_tx_bytes += bufs[i]->current_length; - gso_fixup_segmented_buf (vm, bufs[i], tcp_seq, is_l2, is_ip6, gho, &c, - tcp_flags); + gso_fixup_segmented_buf (vm, bufs[i], tcp_seq, is_l2, oflags, hdr_sz, + l4_hdr_sz, &c, tcp_flags, 0, NULL); vec_free (bufs); return n_tx_bytes; diff --git a/src/vnet/gso/node.c b/src/vnet/gso/node.c index 910f1585e7f..c1d4459476e 100644 --- a/src/vnet/gso/node.c +++ b/src/vnet/gso/node.c @@ -80,113 +80,108 @@ format_gso_trace (u8 * s, va_list * args) return s; } -static_always_inline u16 -tso_segment_ipip_tunnel_fixup (vlib_main_t * vm, - vnet_interface_per_thread_data_t * ptd, - vlib_buffer_t * sb0, - generic_header_offset_t * gho) +static_always_inline void +tso_segment_ipip_tunnel_fixup (vlib_main_t *vm, + vnet_interface_per_thread_data_t *ptd, + vlib_buffer_t *sb0) { u16 n_tx_bufs = vec_len (ptd->split_buffers); - u16 i = 0, n_tx_bytes = 0; + u16 i = 0; while (i < n_tx_bufs) { vlib_buffer_t *b0 = vlib_get_buffer (vm, ptd->split_buffers[i]); + i16 outer_l3_hdr_offset = vnet_buffer2 (b0)->outer_l3_hdr_offset; + i16 l3_hdr_offset = vnet_buffer (b0)->l3_hdr_offset; - ip4_header_t *ip4 = - (ip4_header_t *) (vlib_buffer_get_current (b0) + - gho->outer_l3_hdr_offset); - ip6_header_t *ip6 = - (ip6_header_t *) (vlib_buffer_get_current (b0) + - gho->outer_l3_hdr_offset); + ip4_header_t *ip4 = (ip4_header_t *) (b0->data + outer_l3_hdr_offset); + ip6_header_t *ip6 = (ip6_header_t *) (b0->data + outer_l3_hdr_offset); - if (gho->gho_flags & GHO_F_OUTER_IP4) + if (vnet_buffer (b0)->oflags & VNET_BUFFER_OFFLOAD_F_OUTER_IP_CKSUM) { - ip4->length = - clib_host_to_net_u16 (b0->current_length - - gho->outer_l3_hdr_offset); + ip4->length = clib_host_to_net_u16 ( + b0->current_length - (outer_l3_hdr_offset - b0->current_data)); ip4->checksum = ip4_header_checksum (ip4); + vnet_buffer_offload_flags_clear ( + b0, VNET_BUFFER_OFFLOAD_F_OUTER_IP_CKSUM | + VNET_BUFFER_OFFLOAD_F_TNL_IPIP); } - else if (gho->gho_flags & GHO_F_OUTER_IP6) + else { - ip6->payload_length = - clib_host_to_net_u16 (b0->current_length - - gho->outer_l4_hdr_offset); + ip6->payload_length = clib_host_to_net_u16 ( + b0->current_length - (l3_hdr_offset - b0->current_data)); + vnet_buffer_offload_flags_clear (b0, VNET_BUFFER_OFFLOAD_F_TNL_IPIP); } - n_tx_bytes += gho->outer_hdr_sz; i++; } - return n_tx_bytes; } static_always_inline void -tso_segment_vxlan_tunnel_headers_fixup (vlib_main_t * vm, vlib_buffer_t * b, - generic_header_offset_t * gho) +tso_segment_vxlan_tunnel_headers_fixup (vlib_main_t *vm, vlib_buffer_t *b) { - u8 proto = 0; ip4_header_t *ip4 = 0; ip6_header_t *ip6 = 0; udp_header_t *udp = 0; + i16 outer_l3_hdr_offset = vnet_buffer2 (b)->outer_l3_hdr_offset; + i16 outer_l4_hdr_offset = vnet_buffer2 (b)->outer_l4_hdr_offset; - ip4 = - (ip4_header_t *) (vlib_buffer_get_current (b) + gho->outer_l3_hdr_offset); - ip6 = - (ip6_header_t *) (vlib_buffer_get_current (b) + gho->outer_l3_hdr_offset); - udp = - (udp_header_t *) (vlib_buffer_get_current (b) + gho->outer_l4_hdr_offset); + ip4 = (ip4_header_t *) (b->data + outer_l3_hdr_offset); + ip6 = (ip6_header_t *) (b->data + outer_l3_hdr_offset); + udp = (udp_header_t *) (b->data + outer_l4_hdr_offset); - if (gho->gho_flags & GHO_F_OUTER_IP4) + if (vnet_buffer (b)->oflags & VNET_BUFFER_OFFLOAD_F_OUTER_IP_CKSUM) { - proto = ip4->protocol; - ip4->length = - clib_host_to_net_u16 (b->current_length - gho->outer_l3_hdr_offset); + ip4->length = clib_host_to_net_u16 ( + b->current_length - (outer_l3_hdr_offset - b->current_data)); ip4->checksum = ip4_header_checksum (ip4); + if (vnet_buffer (b)->oflags & VNET_BUFFER_OFFLOAD_F_OUTER_UDP_CKSUM) + { + udp->length = clib_host_to_net_u16 ( + b->current_length - (outer_l4_hdr_offset - b->current_data)); + // udp checksum is 0, in udp tunnel + udp->checksum = 0; + } + vnet_buffer_offload_flags_clear ( + b, VNET_BUFFER_OFFLOAD_F_OUTER_IP_CKSUM | + VNET_BUFFER_OFFLOAD_F_OUTER_UDP_CKSUM | + VNET_BUFFER_OFFLOAD_F_TNL_VXLAN); } - else if (gho->gho_flags & GHO_F_OUTER_IP6) - { - proto = ip6->protocol; - ip6->payload_length = - clib_host_to_net_u16 (b->current_length - gho->outer_l4_hdr_offset); - } - if (proto == IP_PROTOCOL_UDP) + else { - int bogus; - udp->length = - clib_host_to_net_u16 (b->current_length - gho->outer_l4_hdr_offset); - udp->checksum = 0; - if (gho->gho_flags & GHO_F_OUTER_IP6) + ip6->payload_length = clib_host_to_net_u16 ( + b->current_length - (outer_l4_hdr_offset - b->current_data)); + + if (vnet_buffer (b)->oflags & VNET_BUFFER_OFFLOAD_F_OUTER_UDP_CKSUM) { + int bogus; + udp->length = ip6->payload_length; + // udp checksum is 0, in udp tunnel + udp->checksum = 0; udp->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus); + vnet_buffer_offload_flags_clear ( + b, VNET_BUFFER_OFFLOAD_F_OUTER_UDP_CKSUM | + VNET_BUFFER_OFFLOAD_F_TNL_VXLAN); } - else if (gho->gho_flags & GHO_F_OUTER_IP4) - { - udp->checksum = ip4_tcp_udp_compute_checksum (vm, b, ip4); - } - /* FIXME: it should be OUTER_UDP_CKSUM */ - vnet_buffer_offload_flags_clear (b, VNET_BUFFER_OFFLOAD_F_UDP_CKSUM); } } -static_always_inline u16 -tso_segment_vxlan_tunnel_fixup (vlib_main_t * vm, - vnet_interface_per_thread_data_t * ptd, - vlib_buffer_t * sb0, - generic_header_offset_t * gho) +static_always_inline void +tso_segment_vxlan_tunnel_fixup (vlib_main_t *vm, + vnet_interface_per_thread_data_t *ptd, + vlib_buffer_t *sb0) { u16 n_tx_bufs = vec_len (ptd->split_buffers); - u16 i = 0, n_tx_bytes = 0; + u16 i = 0; while (i < n_tx_bufs) { vlib_buffer_t *b0 = vlib_get_buffer (vm, ptd->split_buffers[i]); - tso_segment_vxlan_tunnel_headers_fixup (vm, b0, gho); - n_tx_bytes += gho->outer_hdr_sz; + tso_segment_vxlan_tunnel_headers_fixup (vm, b0); i++; } - return n_tx_bytes; } static_always_inline u16 @@ -682,32 +677,10 @@ vnet_gso_node_inline (vlib_main_t * vm, to_next -= 1; n_left_to_next += 1; /* undo the counting. */ - generic_header_offset_t gho = { 0 }; u32 n_tx_bytes = 0; - u32 inner_is_ip6 = is_ip6; - - vnet_generic_header_offset_parser (b[0], &gho, is_l2, - is_ip4, is_ip6); - - if (PREDICT_FALSE (gho.gho_flags & GHO_F_TUNNEL)) - { - if (PREDICT_FALSE - (gho.gho_flags & (GHO_F_GRE_TUNNEL | - GHO_F_GENEVE_TUNNEL))) - { - /* not supported yet */ - drop_one_buffer_and_count (vm, vnm, node, from - 1, - hi->sw_if_index, - GSO_ERROR_UNHANDLED_TYPE); - b += 1; - continue; - } - inner_is_ip6 = (gho.gho_flags & GHO_F_IP6) != 0; - } - - n_tx_bytes = gso_segment_buffer_inline (vm, ptd, b[0], &gho, - is_l2, inner_is_ip6); + n_tx_bytes = + gso_segment_buffer_inline (vm, ptd, b[0], is_l2); if (PREDICT_FALSE (n_tx_bytes == 0)) { @@ -718,19 +691,15 @@ vnet_gso_node_inline (vlib_main_t * vm, continue; } - - if (PREDICT_FALSE (gho.gho_flags & GHO_F_VXLAN_TUNNEL)) + if (PREDICT_FALSE (vnet_buffer (b[0])->oflags & + VNET_BUFFER_OFFLOAD_F_TNL_VXLAN)) { - n_tx_bytes += - tso_segment_vxlan_tunnel_fixup (vm, ptd, b[0], &gho); + tso_segment_vxlan_tunnel_fixup (vm, ptd, b[0]); } - else - if (PREDICT_FALSE - (gho.gho_flags & (GHO_F_IPIP_TUNNEL | - GHO_F_IPIP6_TUNNEL))) + else if (PREDICT_FALSE (vnet_buffer (b[0])->oflags & + VNET_BUFFER_OFFLOAD_F_TNL_IPIP)) { - n_tx_bytes += - tso_segment_ipip_tunnel_fixup (vm, ptd, b[0], &gho); + tso_segment_ipip_tunnel_fixup (vm, ptd, b[0]); } u16 n_tx_bufs = vec_len (ptd->split_buffers); @@ -744,7 +713,6 @@ vnet_gso_node_inline (vlib_main_t * vm, { sbi0 = to_next[0] = from_seg[0]; sb0 = vlib_get_buffer (vm, sbi0); - vnet_buffer_offload_flags_clear (sb0, 0x7F); ASSERT (sb0->current_length > 0); to_next += 1; from_seg += 1; diff --git a/src/vnet/interface_api.c b/src/vnet/interface_api.c index 2995836672d..c727e519138 100644 --- a/src/vnet/interface_api.c +++ b/src/vnet/interface_api.c @@ -1020,21 +1020,19 @@ vl_api_sw_interface_set_interface_name_t_handler ( { vl_api_sw_interface_set_interface_name_reply_t *rmp; vnet_main_t *vnm = vnet_get_main (); - u32 sw_if_index = ntohl (mp->sw_if_index); - vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index); clib_error_t *error; int rv = 0; + VALIDATE_SW_IF_INDEX (mp); + + u32 sw_if_index = ntohl (mp->sw_if_index); + vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index); + if (mp->name[0] == 0) { rv = VNET_API_ERROR_INVALID_VALUE; goto out; } - if (si == 0) - { - rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; - goto out; - } error = vnet_rename_interface (vnm, si->hw_if_index, (char *) mp->name); if (error) @@ -1044,6 +1042,7 @@ vl_api_sw_interface_set_interface_name_t_handler ( } out: + BAD_SW_IF_INDEX_LABEL; REPLY_MACRO (VL_API_SW_INTERFACE_SET_INTERFACE_NAME_REPLY); } diff --git a/src/vnet/ip-neighbor/ip_neighbor.c b/src/vnet/ip-neighbor/ip_neighbor.c index d340037a15d..614b78489cd 100644 --- a/src/vnet/ip-neighbor/ip_neighbor.c +++ b/src/vnet/ip-neighbor/ip_neighbor.c @@ -460,6 +460,7 @@ ip_neighbor_destroy (ip_neighbor_t * ipn) af = ip_neighbor_get_af (ipn); IP_NEIGHBOR_DBG ("free: %U", format_ip_neighbor, + vlib_time_now (vlib_get_main ()), ip_neighbor_get_index (ipn)); ip_neighbor_publish (ip_neighbor_get_index (ipn), @@ -944,20 +945,20 @@ ip_neighbor_show_sorted_i (vlib_main_t * vm, vlib_cli_command_t * cmd, ip_address_family_t af) { ip_neighbor_elt_t *elt, *head; + f64 now; head = pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[af]); + now = vlib_time_now (vm); - - vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP", - "Flags", "Ethernet", "Interface"); + vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Age", "IP", "Flags", + "Ethernet", "Interface"); /* the list is time sorted, newest first, so start from the back * and work forwards. Stop when we get to one that is alive */ - clib_llist_foreach_reverse(ip_neighbor_elt_pool, - ipne_anchor, head, elt, - ({ - vlib_cli_output (vm, "%U", format_ip_neighbor, elt->ipne_index); - })); + clib_llist_foreach_reverse (ip_neighbor_elt_pool, ipne_anchor, head, elt, ({ + vlib_cli_output (vm, "%U", format_ip_neighbor, + now, elt->ipne_index); + })); return (NULL); } @@ -969,6 +970,7 @@ ip_neighbor_show_i (vlib_main_t * vm, { index_t *ipni, *ipnis = NULL; u32 sw_if_index; + f64 now; /* Filter entries by interface if given. */ sw_if_index = ~0; @@ -976,14 +978,15 @@ ip_neighbor_show_i (vlib_main_t * vm, &sw_if_index); ipnis = ip_neighbor_entries (sw_if_index, af); + now = vlib_time_now (vm); if (ipnis) - vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP", - "Flags", "Ethernet", "Interface"); + vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Age", "IP", "Flags", + "Ethernet", "Interface"); vec_foreach (ipni, ipnis) { - vlib_cli_output (vm, "%U", format_ip_neighbor, *ipni); + vlib_cli_output (vm, "%U", format_ip_neighbor, now, *ipni); } vec_free (ipnis); @@ -1573,13 +1576,12 @@ ip_neighbour_age_out (index_t ipni, f64 now, f64 * wait) if (ttl > ipndb_age) { - IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d", - format_ip_neighbor, ipni, now, - ipn->ipn_time_last_updated, ipndb_age); + IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d", format_ip_neighbor, now, ipni, + now, ipn->ipn_time_last_updated, ipndb_age); if (ipn->ipn_n_probes > 2) { /* 3 strikes and yea-re out */ - IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, ipni); + IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, now, ipni); *wait = 1; return (IP_NEIGHBOR_AGE_DEAD); } diff --git a/src/vnet/ip-neighbor/ip_neighbor_types.c b/src/vnet/ip-neighbor/ip_neighbor_types.c index 39039a48249..a6f3c26d42f 100644 --- a/src/vnet/ip-neighbor/ip_neighbor_types.c +++ b/src/vnet/ip-neighbor/ip_neighbor_types.c @@ -68,19 +68,18 @@ format_ip_neighbor_watcher (u8 * s, va_list * va) u8 * format_ip_neighbor (u8 * s, va_list * va) { + f64 now = va_arg (*va, f64); index_t ipni = va_arg (*va, index_t); ip_neighbor_t *ipn; ipn = ip_neighbor_get (ipni); - return (format (s, "%=12U%=40U%=6U%=20U%U", - format_vlib_time, vlib_get_main (), - ipn->ipn_time_last_updated, - format_ip_address, &ipn->ipn_key->ipnk_ip, - format_ip_neighbor_flags, ipn->ipn_flags, - format_mac_address_t, &ipn->ipn_mac, - format_vnet_sw_if_index_name, vnet_get_main (), - ipn->ipn_key->ipnk_sw_if_index)); + return ( + format (s, "%=12U%=40U%=6U%=20U%U", format_vlib_time, vlib_get_main (), + now - ipn->ipn_time_last_updated, format_ip_address, + &ipn->ipn_key->ipnk_ip, format_ip_neighbor_flags, ipn->ipn_flags, + format_mac_address_t, &ipn->ipn_mac, format_vnet_sw_if_index_name, + vnet_get_main (), ipn->ipn_key->ipnk_sw_if_index)); } static void diff --git a/src/vnet/ip/icmp6.c b/src/vnet/ip/icmp6.c index b095f679cc8..f93ebce4bf1 100644 --- a/src/vnet/ip/icmp6.c +++ b/src/vnet/ip/icmp6.c @@ -338,7 +338,7 @@ ip6_icmp_error (vlib_main_t * vm, if (throttle_check (&icmp_throttle, thread_index, r0, seed)) { - vlib_error_count (vm, node->node_index, ICMP4_ERROR_DROP, 1); + vlib_error_count (vm, node->node_index, ICMP6_ERROR_DROP, 1); from += 1; n_left_from -= 1; continue; diff --git a/src/vnet/ip/lookup.c b/src/vnet/ip/lookup.c index c225c222a38..c0fa430e0aa 100644 --- a/src/vnet/ip/lookup.c +++ b/src/vnet/ip/lookup.c @@ -603,6 +603,8 @@ VLIB_CLI_COMMAND (vlib_cli_show_ip6_command, static) = { * @cliexcmd{ip route add 7.0.0.1/32 via 6.0.0.2 GigabitEthernet2/0/0 weight 3} * To add a route to a particular FIB table (VRF), use: * @cliexcmd{ip route add 172.16.24.0/24 table 7 via GigabitEthernet2/0/0} + * To add a route to drop the traffic: + * @cliexcmd{ip route add 172.16.24.0/24 table 100 via 127.0.0.1 drop} ?*/ VLIB_CLI_COMMAND (ip_route_command, static) = { .path = "ip route", @@ -612,7 +614,7 @@ VLIB_CLI_COMMAND (ip_route_command, static) = { "<value>] [udp-encap <value>] [ip4-lookup-in-table <value>] " "[ip6-lookup-in-table <value>] [mpls-lookup-in-table <value>] " "[resolve-via-host] [resolve-via-connected] [rx-ip4|rx-ip6 " - "<interface>] [out-labels <value value value>]", + "<interface>] [out-labels <value value value>] [drop]", .function = vnet_ip_route_cmd, .is_mp_safe = 1, }; diff --git a/src/vnet/ipsec/ah_decrypt.c b/src/vnet/ipsec/ah_decrypt.c index 918ebf03f67..ec4db0fed57 100644 --- a/src/vnet/ipsec/ah_decrypt.c +++ b/src/vnet/ipsec/ah_decrypt.c @@ -500,10 +500,10 @@ ah_decrypt_init (vlib_main_t *vm) { ipsec_main_t *im = &ipsec_main; - im->ah4_dec_fq_index = - vlib_frame_queue_main_init (ah4_decrypt_node.index, 0); - im->ah6_dec_fq_index = - vlib_frame_queue_main_init (ah6_decrypt_node.index, 0); + im->ah4_dec_fq_index = vlib_frame_queue_main_init (ah4_decrypt_node.index, + im->handoff_queue_size); + im->ah6_dec_fq_index = vlib_frame_queue_main_init (ah6_decrypt_node.index, + im->handoff_queue_size); return 0; } diff --git a/src/vnet/ipsec/ah_encrypt.c b/src/vnet/ipsec/ah_encrypt.c index 960327f071d..86694660878 100644 --- a/src/vnet/ipsec/ah_encrypt.c +++ b/src/vnet/ipsec/ah_encrypt.c @@ -490,10 +490,10 @@ ah_encrypt_init (vlib_main_t *vm) { ipsec_main_t *im = &ipsec_main; - im->ah4_enc_fq_index = - vlib_frame_queue_main_init (ah4_encrypt_node.index, 0); - im->ah6_enc_fq_index = - vlib_frame_queue_main_init (ah6_encrypt_node.index, 0); + im->ah4_enc_fq_index = vlib_frame_queue_main_init (ah4_encrypt_node.index, + im->handoff_queue_size); + im->ah6_enc_fq_index = vlib_frame_queue_main_init (ah6_encrypt_node.index, + im->handoff_queue_size); return 0; } diff --git a/src/vnet/ipsec/esp_decrypt.c b/src/vnet/ipsec/esp_decrypt.c index 26d8ca1deee..01b2d2971b0 100644 --- a/src/vnet/ipsec/esp_decrypt.c +++ b/src/vnet/ipsec/esp_decrypt.c @@ -1675,14 +1675,14 @@ esp_decrypt_init (vlib_main_t *vm) { ipsec_main_t *im = &ipsec_main; - im->esp4_dec_fq_index = - vlib_frame_queue_main_init (esp4_decrypt_node.index, 0); - im->esp6_dec_fq_index = - vlib_frame_queue_main_init (esp6_decrypt_node.index, 0); - im->esp4_dec_tun_fq_index = - vlib_frame_queue_main_init (esp4_decrypt_tun_node.index, 0); - im->esp6_dec_tun_fq_index = - vlib_frame_queue_main_init (esp6_decrypt_tun_node.index, 0); + im->esp4_dec_fq_index = vlib_frame_queue_main_init (esp4_decrypt_node.index, + im->handoff_queue_size); + im->esp6_dec_fq_index = vlib_frame_queue_main_init (esp6_decrypt_node.index, + im->handoff_queue_size); + im->esp4_dec_tun_fq_index = vlib_frame_queue_main_init ( + esp4_decrypt_tun_node.index, im->handoff_queue_size); + im->esp6_dec_tun_fq_index = vlib_frame_queue_main_init ( + esp6_decrypt_tun_node.index, im->handoff_queue_size); return 0; } diff --git a/src/vnet/ipsec/esp_encrypt.c b/src/vnet/ipsec/esp_encrypt.c index dd47053874c..544ff7b6aa8 100644 --- a/src/vnet/ipsec/esp_encrypt.c +++ b/src/vnet/ipsec/esp_encrypt.c @@ -1445,16 +1445,16 @@ esp_encrypt_init (vlib_main_t *vm) { ipsec_main_t *im = &ipsec_main; - im->esp4_enc_fq_index = - vlib_frame_queue_main_init (esp4_encrypt_node.index, 0); - im->esp6_enc_fq_index = - vlib_frame_queue_main_init (esp6_encrypt_node.index, 0); - im->esp4_enc_tun_fq_index = - vlib_frame_queue_main_init (esp4_encrypt_tun_node.index, 0); - im->esp6_enc_tun_fq_index = - vlib_frame_queue_main_init (esp6_encrypt_tun_node.index, 0); - im->esp_mpls_enc_tun_fq_index = - vlib_frame_queue_main_init (esp_mpls_encrypt_tun_node.index, 0); + im->esp4_enc_fq_index = vlib_frame_queue_main_init (esp4_encrypt_node.index, + im->handoff_queue_size); + im->esp6_enc_fq_index = vlib_frame_queue_main_init (esp6_encrypt_node.index, + im->handoff_queue_size); + im->esp4_enc_tun_fq_index = vlib_frame_queue_main_init ( + esp4_encrypt_tun_node.index, im->handoff_queue_size); + im->esp6_enc_tun_fq_index = vlib_frame_queue_main_init ( + esp6_encrypt_tun_node.index, im->handoff_queue_size); + im->esp_mpls_enc_tun_fq_index = vlib_frame_queue_main_init ( + esp_mpls_encrypt_tun_node.index, im->handoff_queue_size); return 0; } diff --git a/src/vnet/ipsec/ipsec.c b/src/vnet/ipsec/ipsec.c index f8c39c327ed..8b43dd23cc8 100644 --- a/src/vnet/ipsec/ipsec.c +++ b/src/vnet/ipsec/ipsec.c @@ -663,6 +663,7 @@ ipsec_config (vlib_main_t *vm, unformat_input_t *input) u32 ipsec_spd_fp_num_buckets; bool fp_spd_ip4_enabled = false; bool fp_spd_ip6_enabled = false; + u32 handoff_queue_size; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { @@ -758,6 +759,11 @@ ipsec_config (vlib_main_t *vm, unformat_input_t *input) ipsec_tun_table_init (AF_IP6, table_size, n_buckets); } + else if (unformat (input, "async-handoff-queue-size %d", + &handoff_queue_size)) + { + im->handoff_queue_size = handoff_queue_size; + } else return clib_error_return (0, "unknown input `%U'", format_unformat_error, input); diff --git a/src/vnet/ipsec/ipsec.h b/src/vnet/ipsec/ipsec.h index 4aa09d7560e..9ab054cf2a9 100644 --- a/src/vnet/ipsec/ipsec.h +++ b/src/vnet/ipsec/ipsec.h @@ -248,6 +248,8 @@ typedef struct u32 esp4_dec_tun_fq_index; u32 esp6_dec_tun_fq_index; + u32 handoff_queue_size; + /* Number of buckets for flow cache */ u32 ipsec4_out_spd_hash_num_buckets; u32 ipsec4_out_spd_flow_cache_entries; diff --git a/src/vnet/ipsec/ipsec_input.c b/src/vnet/ipsec/ipsec_input.c index 6ccc0be2622..48f7deadda3 100644 --- a/src/vnet/ipsec/ipsec_input.c +++ b/src/vnet/ipsec/ipsec_input.c @@ -274,6 +274,159 @@ ip6_addr_match_range (ip6_address_t * a, ip6_address_t * la, } always_inline void +ipsec_collect_ah_trace (vlib_buffer_t **b, vlib_node_runtime_t *node, + vlib_main_t *vm, ip4_header_t *ip0, ah_header_t *ah0, + u8 has_space0, ipsec_spd_t *spd0, ipsec_policy_t *p0, + u32 pi0) +{ + if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) && + PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED)) + { + ipsec_input_trace_t *tr = vlib_add_trace (vm, node, b[0], sizeof (*tr)); + + tr->proto = ip0->protocol; + tr->sa_id = p0 ? p0->sa_id : ~0; + tr->spi = has_space0 ? clib_net_to_host_u32 (ah0->spi) : ~0; + tr->seq = has_space0 ? clib_net_to_host_u32 (ah0->seq_no) : ~0; + tr->spd = spd0->id; + tr->policy_index = pi0; + } +} + +always_inline void +ipsec_ah_packet_process (vlib_main_t *vm, ipsec_main_t *im, ip4_header_t *ip0, + ah_header_t *ah0, u32 thread_index, ipsec_spd_t *spd0, + vlib_buffer_t **b, vlib_node_runtime_t *node, + u64 *ipsec_bypassed, u64 *ipsec_dropped, + u64 *ipsec_matched, u64 *ipsec_unprocessed, u16 *next) + +{ + ipsec_policy_t *p0 = NULL; + u32 pi0 = ~0; + u8 has_space0; + /* if flow cache is enabled, first search through flow cache for a + * policy match and revert back to linear search on failure */ + bool search_flow_cache = im->input_flow_cache_flag; + + while (1) + { + if (search_flow_cache) + { + p0 = ipsec4_input_spd_find_flow_cache_entry ( + im, ip0->src_address.as_u32, ip0->dst_address.as_u32, + IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT); + } + else + { + p0 = ipsec_input_protect_policy_match ( + spd0, clib_net_to_host_u32 (ip0->src_address.as_u32), + clib_net_to_host_u32 (ip0->dst_address.as_u32), + clib_net_to_host_u32 (ah0->spi)); + } + + has_space0 = vlib_buffer_has_space (b[0], (clib_address_t) (ah0 + 1) - + (clib_address_t) ip0); + + if (PREDICT_TRUE ((p0 != NULL) & (has_space0))) + { + *ipsec_matched += 1; + pi0 = p0 - im->policies; + vlib_increment_combined_counter (&ipsec_spd_policy_counters, + thread_index, pi0, 1, + clib_net_to_host_u16 (ip0->length)); + + vnet_buffer (b[0])->ipsec.sad_index = p0->sa_index; + next[0] = im->ah4_decrypt_next_index; + ipsec_collect_ah_trace (b, node, vm, ip0, ah0, has_space0, spd0, p0, + pi0); + return; + } + else + { + p0 = 0; + pi0 = ~0; + } + if (search_flow_cache) + { + p0 = ipsec4_input_spd_find_flow_cache_entry ( + im, ip0->src_address.as_u32, ip0->dst_address.as_u32, + IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS); + } + + else + { + p0 = ipsec_input_policy_match ( + spd0, clib_net_to_host_u32 (ip0->src_address.as_u32), + clib_net_to_host_u32 (ip0->dst_address.as_u32), + IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS); + } + + if (PREDICT_TRUE ((p0 != NULL))) + { + *ipsec_bypassed += 1; + pi0 = p0 - im->policies; + vlib_increment_combined_counter (&ipsec_spd_policy_counters, + thread_index, pi0, 1, + clib_net_to_host_u16 (ip0->length)); + ipsec_collect_ah_trace (b, node, vm, ip0, ah0, has_space0, spd0, p0, + pi0); + return; + } + else + { + p0 = 0; + pi0 = ~0; + }; + + if (search_flow_cache) + { + p0 = ipsec4_input_spd_find_flow_cache_entry ( + im, ip0->src_address.as_u32, ip0->dst_address.as_u32, + IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD); + } + + else + { + p0 = ipsec_input_policy_match ( + spd0, clib_net_to_host_u32 (ip0->src_address.as_u32), + clib_net_to_host_u32 (ip0->dst_address.as_u32), + IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD); + } + + if (PREDICT_TRUE ((p0 != NULL))) + { + *ipsec_dropped += 1; + pi0 = p0 - im->policies; + vlib_increment_combined_counter (&ipsec_spd_policy_counters, + thread_index, pi0, 1, + clib_net_to_host_u16 (ip0->length)); + + next[0] = IPSEC_INPUT_NEXT_DROP; + ipsec_collect_ah_trace (b, node, vm, ip0, ah0, has_space0, spd0, p0, + pi0); + return; + } + else + { + p0 = 0; + pi0 = ~0; + }; + /* flow cache search failed, retry with linear search */ + if (search_flow_cache && p0 == NULL) + { + search_flow_cache = false; + } + else if (search_flow_cache == false && p0 == NULL) + { + /* Drop by default if no match on PROTECT, BYPASS or DISCARD */ + *ipsec_unprocessed += 1; + next[0] = IPSEC_INPUT_NEXT_DROP; + return; + } + } +} + +always_inline void ipsec_esp_packet_process (vlib_main_t *vm, ipsec_main_t *im, ip4_header_t *ip0, esp_header_t *esp0, u32 thread_index, ipsec_spd_t *spd0, vlib_buffer_t **b, @@ -299,10 +452,11 @@ ipsec_esp_packet_process (vlib_main_t *vm, ipsec_main_t *im, ip4_header_t *ip0, search_flow_cache = im->input_flow_cache_flag; udp_or_esp: - /* SPI ID field in the ESP header MUST NOT be a zero value */ if (esp0->spi == 0) { - /* Drop the packet if SPI ID is zero */ + /* RFC 4303, section 2.1: The SPI value of zero (0 is reserved for + * local, implementation-specific use and MUST NOT be sent on the wire. + */ *ipsec_unprocessed += 1; next[0] = IPSEC_INPUT_NEXT_DROP; return; @@ -523,15 +677,12 @@ VLIB_NODE_FN (ipsec4_input_node) (vlib_main_t * vm, while (n_left_from > 0) { - u32 next32, pi0; + u32 next32; ip4_header_t *ip0; esp_header_t *esp0 = NULL; ah_header_t *ah0; ip4_ipsec_config_t *c0; ipsec_spd_t *spd0; - ipsec_policy_t *p0 = NULL; - u8 has_space0; - bool search_flow_cache = false; if (n_left_from > 2) { @@ -552,12 +703,10 @@ VLIB_NODE_FN (ipsec4_input_node) (vlib_main_t * vm, udp_header_t *udp0 = NULL; udp0 = (udp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0)); - /* As per rfc3948 in UDP Encapsulated Header, UDP checksum must be - * Zero, and receivers must not depen upon UPD checksum. - * inside ESP header , SPI ID value MUST NOT be a zero value - * */ - - if (udp0->checksum == 0) + /* RFC5996 Section 2.23 "Port 4500 is reserved for + * UDP-encapsulated ESP and IKE." + */ + if (clib_host_to_net_u16 (4500) == udp0->dst_port) { esp0 = (esp_header_t *) ((u8 *) udp0 + sizeof (udp_header_t)); @@ -582,140 +731,9 @@ VLIB_NODE_FN (ipsec4_input_node) (vlib_main_t * vm, { ah0 = (ah_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0)); - // if flow cache is enabled, first search through flow cache for a - // policy match and revert back to linear search on failure - search_flow_cache = im->input_flow_cache_flag; - - ah: - if (search_flow_cache) - { - p0 = ipsec4_input_spd_find_flow_cache_entry ( - im, ip0->src_address.as_u32, ip0->dst_address.as_u32, - IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT); - } - - else - { - p0 = ipsec_input_protect_policy_match ( - spd0, clib_net_to_host_u32 (ip0->src_address.as_u32), - clib_net_to_host_u32 (ip0->dst_address.as_u32), - clib_net_to_host_u32 (ah0->spi)); - } - - has_space0 = - vlib_buffer_has_space (b[0], - (clib_address_t) (ah0 + 1) - - (clib_address_t) ip0); - - if (PREDICT_TRUE ((p0 != NULL) & (has_space0))) - { - ipsec_matched += 1; - - pi0 = p0 - im->policies; - vlib_increment_combined_counter - (&ipsec_spd_policy_counters, - thread_index, pi0, 1, clib_net_to_host_u16 (ip0->length)); - - vnet_buffer (b[0])->ipsec.sad_index = p0->sa_index; - next[0] = im->ah4_decrypt_next_index; - goto trace1; - } - else - { - p0 = 0; - pi0 = ~0; - } - - if (search_flow_cache) - { - p0 = ipsec4_input_spd_find_flow_cache_entry ( - im, ip0->src_address.as_u32, ip0->dst_address.as_u32, - IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS); - } - - else - { - p0 = ipsec_input_policy_match ( - spd0, clib_net_to_host_u32 (ip0->src_address.as_u32), - clib_net_to_host_u32 (ip0->dst_address.as_u32), - IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS); - } - - if (PREDICT_TRUE ((p0 != NULL))) - { - ipsec_bypassed += 1; - - pi0 = p0 - im->policies; - vlib_increment_combined_counter ( - &ipsec_spd_policy_counters, thread_index, pi0, 1, - clib_net_to_host_u16 (ip0->length)); - - goto trace1; - } - else - { - p0 = 0; - pi0 = ~0; - }; - - if (search_flow_cache) - { - p0 = ipsec4_input_spd_find_flow_cache_entry ( - im, ip0->src_address.as_u32, ip0->dst_address.as_u32, - IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD); - } - - else - { - p0 = ipsec_input_policy_match ( - spd0, clib_net_to_host_u32 (ip0->src_address.as_u32), - clib_net_to_host_u32 (ip0->dst_address.as_u32), - IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD); - } - - if (PREDICT_TRUE ((p0 != NULL))) - { - ipsec_dropped += 1; - - pi0 = p0 - im->policies; - vlib_increment_combined_counter ( - &ipsec_spd_policy_counters, thread_index, pi0, 1, - clib_net_to_host_u16 (ip0->length)); - - next[0] = IPSEC_INPUT_NEXT_DROP; - goto trace1; - } - else - { - p0 = 0; - pi0 = ~0; - }; - - // flow cache search failed, retry with linear search - if (search_flow_cache && p0 == NULL) - { - search_flow_cache = false; - goto ah; - } - - /* Drop by default if no match on PROTECT, BYPASS or DISCARD */ - ipsec_unprocessed += 1; - next[0] = IPSEC_INPUT_NEXT_DROP; - - trace1: - if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) && - PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED)) - { - ipsec_input_trace_t *tr = - vlib_add_trace (vm, node, b[0], sizeof (*tr)); - - tr->proto = ip0->protocol; - tr->sa_id = p0 ? p0->sa_id : ~0; - tr->spi = has_space0 ? clib_net_to_host_u32 (ah0->spi) : ~0; - tr->seq = has_space0 ? clib_net_to_host_u32 (ah0->seq_no) : ~0; - tr->spd = spd0->id; - tr->policy_index = pi0; - } + ipsec_ah_packet_process (vm, im, ip0, ah0, thread_index, spd0, b, + node, &ipsec_bypassed, &ipsec_dropped, + &ipsec_matched, &ipsec_unprocessed, next); } else { diff --git a/src/vnet/pg/input.c b/src/vnet/pg/input.c index f81485de65f..321472c4d85 100644 --- a/src/vnet/pg/input.c +++ b/src/vnet/pg/input.c @@ -1578,7 +1578,7 @@ fill_buffer_offload_flags (vlib_main_t *vm, u32 *buffers, u32 n_buffers, (VNET_BUFFER_F_IS_IP4 | VNET_BUFFER_F_L2_HDR_OFFSET_VALID | VNET_BUFFER_F_L3_HDR_OFFSET_VALID | VNET_BUFFER_F_L4_HDR_OFFSET_VALID); - if (buffer_oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM) + if (buffer_oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM || gso_enabled) oflags |= VNET_BUFFER_OFFLOAD_F_IP_CKSUM; } else if (PREDICT_TRUE (ethertype == ETHERNET_TYPE_IP6)) @@ -1596,7 +1596,7 @@ fill_buffer_offload_flags (vlib_main_t *vm, u32 *buffers, u32 n_buffers, if (l4_proto == IP_PROTOCOL_TCP) { - if (buffer_oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM) + if (buffer_oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM || gso_enabled) oflags |= VNET_BUFFER_OFFLOAD_F_TCP_CKSUM; /* only set GSO flag for chained buffers */ diff --git a/src/vnet/session/session.c b/src/vnet/session/session.c index 67e7ee39001..2ca867c166f 100644 --- a/src/vnet/session/session.c +++ b/src/vnet/session/session.c @@ -1659,8 +1659,10 @@ session_transport_close (session_t * s) { if (s->session_state == SESSION_STATE_TRANSPORT_CLOSED) session_set_state (s, SESSION_STATE_CLOSED); - /* If transport is already deleted, just free the session */ - else if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED) + /* If transport is already deleted, just free the session. Half-opens + * expected to be already cleaning up at this point */ + else if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED && + !(s->flags & SESSION_F_HALF_OPEN)) session_program_cleanup (s); return; } @@ -1687,7 +1689,8 @@ session_transport_reset (session_t * s) { if (s->session_state == SESSION_STATE_TRANSPORT_CLOSED) session_set_state (s, SESSION_STATE_CLOSED); - else if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED) + else if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED && + !(s->flags & SESSION_F_HALF_OPEN)) session_program_cleanup (s); return; } diff --git a/src/vnet/session/session_lookup.c b/src/vnet/session/session_lookup.c index 9d028dbb28c..ff20bc2d835 100644 --- a/src/vnet/session/session_lookup.c +++ b/src/vnet/session/session_lookup.c @@ -1184,7 +1184,6 @@ session_lookup_connection_wt6 (u32 fib_index, ip6_address_t * lcl, rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6); if (rv == 0) { - ASSERT ((u32) (kv6.value >> 32) == thread_index); if (PREDICT_FALSE ((u32) (kv6.value >> 32) != thread_index)) { *result = SESSION_LOOKUP_RESULT_WRONG_THREAD; diff --git a/src/vnet/udp/udp.c b/src/vnet/udp/udp.c index b3c02510232..9c1121f7cfb 100644 --- a/src/vnet/udp/udp.c +++ b/src/vnet/udp/udp.c @@ -232,18 +232,43 @@ udp_session_get_listener (u32 listener_index) return &us->connection; } +always_inline u16 +udp_compute_checksum (vlib_main_t *vm, vlib_buffer_t *b, u8 csum_offload, + u8 is_ip4) +{ + u16 csum = 0; + + if (csum_offload) + vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_UDP_CKSUM); + else + { + if (is_ip4) + csum = + ip4_tcp_udp_compute_checksum (vm, b, vlib_buffer_get_current (b)); + else + { + int bogus = 0; + csum = ip6_tcp_udp_icmp_compute_checksum ( + vm, b, vlib_buffer_get_current (b), &bogus); + } + } + + return csum; +} + always_inline u32 udp_push_one_header (vlib_main_t *vm, udp_connection_t *uc, vlib_buffer_t *b, u8 is_cless) { + udp_header_t *uh; + b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; /* reuse tcp medatada for now */ vnet_buffer (b)->tcp.connection_index = uc->c_c_index; if (!is_cless) { - vlib_buffer_push_udp (b, uc->c_lcl_port, uc->c_rmt_port, - udp_csum_offload (uc)); + uh = vlib_buffer_push_udp (b, uc->c_lcl_port, uc->c_rmt_port); if (uc->c_is_ip4) vlib_buffer_push_ip4_custom (vm, b, &uc->c_lcl_ip4, &uc->c_rmt_ip4, @@ -263,8 +288,7 @@ udp_push_one_header (vlib_main_t *vm, udp_connection_t *uc, vlib_buffer_t *b, hdr = *(session_dgram_hdr_t *) (data - sizeof (hdr)); /* Local port assumed to be bound, not overwriting it */ - vlib_buffer_push_udp (b, uc->c_lcl_port, hdr.rmt_port, - udp_csum_offload (uc)); + uh = vlib_buffer_push_udp (b, uc->c_lcl_port, hdr.rmt_port); if (uc->c_is_ip4) vlib_buffer_push_ip4_custom (vm, b, &hdr.lcl_ip.ip4, &hdr.rmt_ip.ip4, @@ -279,6 +303,9 @@ udp_push_one_header (vlib_main_t *vm, udp_connection_t *uc, vlib_buffer_t *b, vnet_buffer (b)->tcp.flags |= UDP_CONN_F_LISTEN; } + uh->checksum = + udp_compute_checksum (vm, b, udp_csum_offload (uc), uc->c_is_ip4); + return 0; } diff --git a/src/vnet/udp/udp_inlines.h b/src/vnet/udp/udp_inlines.h index f0dd44f48b5..ceec0b191b1 100644 --- a/src/vnet/udp/udp_inlines.h +++ b/src/vnet/udp/udp_inlines.h @@ -26,7 +26,7 @@ #include <vnet/udp/udp_encap.h> always_inline void * -vlib_buffer_push_udp (vlib_buffer_t * b, u16 sp, u16 dp, u8 offload_csum) +vlib_buffer_push_udp (vlib_buffer_t *b, u16 sp, u16 dp) { udp_header_t *uh; u16 udp_len = sizeof (udp_header_t) + b->current_length; @@ -38,8 +38,6 @@ vlib_buffer_push_udp (vlib_buffer_t * b, u16 sp, u16 dp, u8 offload_csum) uh->dst_port = dp; uh->checksum = 0; uh->length = clib_host_to_net_u16 (udp_len); - if (offload_csum) - vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_UDP_CKSUM); vnet_buffer (b)->l4_hdr_offset = (u8 *) uh - b->data; b->flags |= VNET_BUFFER_F_L4_HDR_OFFSET_VALID; return uh; diff --git a/src/vnet/unix/gdb_funcs.c b/src/vnet/unix/gdb_funcs.c index d6fdc985bd9..a89b7202400 100644 --- a/src/vnet/unix/gdb_funcs.c +++ b/src/vnet/unix/gdb_funcs.c @@ -238,44 +238,44 @@ gdb_show_traces () /* Get active traces from pool. */ - foreach_vlib_main () - { - fmt = "------------------- Start of thread %d %s -------------------\n"; - s = format (s, fmt, index, vlib_worker_threads[index].name); + foreach_vlib_main__ (0 /* no checks */) + { + fmt = "------------------- Start of thread %d %s -------------------\n"; + s = format (s, fmt, index, vlib_worker_threads[index].name); - tm = &this_vlib_main->trace_main; + tm = &this_vlib_main->trace_main; - trace_apply_filter (this_vlib_main); + trace_apply_filter (this_vlib_main); - traces = 0; - pool_foreach (h, tm->trace_buffer_pool) - { - vec_add1 (traces, h[0]); - } + traces = 0; + pool_foreach (h, tm->trace_buffer_pool) + { + vec_add1 (traces, h[0]); + } - if (vec_len (traces) == 0) - { - s = format (s, "No packets in trace buffer\n"); - goto done; - } + if (vec_len (traces) == 0) + { + s = format (s, "No packets in trace buffer\n"); + goto done; + } - /* Sort them by increasing time. */ - vec_sort_with_function (traces, trace_cmp); + /* Sort them by increasing time. */ + vec_sort_with_function (traces, trace_cmp); - for (i = 0; i < vec_len (traces); i++) - { - if (i == max) - { - fformat (stderr, - "Limiting display to %d packets." - " To display more specify max.", - max); - goto done; - } - - s = format (s, "Packet %d\n%U\n\n", i + 1, format_vlib_trace, - vlib_get_first_main (), traces[i]); - } + for (i = 0; i < vec_len (traces); i++) + { + if (i == max) + { + fformat (stderr, + "Limiting display to %d packets." + " To display more specify max.", + max); + goto done; + } + + s = format (s, "Packet %d\n%U\n\n", i + 1, format_vlib_trace, + vlib_get_first_main (), traces[i]); + } done: vec_free (traces); diff --git a/src/vpp-api/python/vpp_papi/vpp_serializer.py b/src/vpp-api/python/vpp_papi/vpp_serializer.py index d724cb33ce9..707bb03b790 100644 --- a/src/vpp-api/python/vpp_papi/vpp_serializer.py +++ b/src/vpp-api/python/vpp_papi/vpp_serializer.py @@ -644,10 +644,15 @@ class VPPType(Packer): else: arg = data[a] kwarg = kwargs[a] if a in kwargs else None - if isinstance(self.packers[i], VPPType): - b += self.packers[i].pack(arg, kwarg) - else: - b += self.packers[i].pack(arg, kwargs) + try: + if isinstance(self.packers[i], VPPType): + b += self.packers[i].pack(arg, kwarg) + else: + b += self.packers[i].pack(arg, kwargs) + except Exception as e: + raise VPPSerializerValueError( + f"Exception while packing {data} for {self.name}.{a}." + ) from e return bytes(b) diff --git a/src/vpp/vnet/main.c b/src/vpp/vnet/main.c index c57efd59a62..dd4f4cc3353 100644 --- a/src/vpp/vnet/main.c +++ b/src/vpp/vnet/main.c @@ -329,6 +329,10 @@ defaulted: unformat_free (&input); + /* if main thread affinity is unspecified, set to current running cpu */ + if (main_core == ~0) + main_core = sched_getcpu (); + /* set process affinity for main thread */ if (main_core != ~0) { diff --git a/src/vppinfra/CMakeLists.txt b/src/vppinfra/CMakeLists.txt index 5878f0612f0..233e75d6e2a 100644 --- a/src/vppinfra/CMakeLists.txt +++ b/src/vppinfra/CMakeLists.txt @@ -14,6 +14,34 @@ enable_language(ASM) ############################################################################## +# find libdl +############################################################################## +vpp_find_path(LIBDL_INCLUDE_DIR dlfcn.h) +vpp_find_library(LIBDL_LIB NAMES dl) + +if (LIBDL_INCLUDE_DIR AND LIBDL_LIB) + message(STATUS "libdl found at ${LIBDL_LIB}") + list(APPEND VPPINFRA_LIBS ${LIBDL_LIB}) +else() + message(FATAL_ERROR "libdl not found") +endif() + +############################################################################## +# find libunwind +############################################################################## +vpp_find_path(LIBUNWIND_INCLUDE_DIR unwind.h) +vpp_find_library(LIBUNWIND_LIB NAMES unwind libunwind) + +if (LIBUNWIND_INCLUDE_DIR AND LIBUNWIND_LIB) + message(STATUS "libunwind found at ${LIBUNWIND_LIB}") + list(APPEND VPPINFRA_LIBS ${LIBUNWIND_LIB}) + add_definitions(-DHAVE_LIBUNWIND=1) +else() + message(WARNING "libunwind not found - stack traces disabled") + add_definitions(-DHAVE_LIBUNWIND=0) +endif() + +############################################################################## # Generate vppinfra/config.h ############################################################################## set(LOG2_CACHE_LINE_BYTES ${VPP_LOG2_CACHE_LINE_SIZE}) @@ -42,12 +70,10 @@ add_definitions(-fvisibility=hidden) set_source_files_properties( cJSON.c jsonformat.c PROPERTIES COMPILE_DEFINITIONS " CJSON_API_VISIBILITY " ) - ############################################################################## # vppinfra sources ############################################################################## set(VPPINFRA_SRCS - backtrace.c bitmap.c bihash_all_vector.c cpu.c @@ -80,6 +106,7 @@ set(VPPINFRA_SRCS rbtree.c serialize.c socket.c + stack.c std-formats.c string.c time.c @@ -142,6 +169,7 @@ set(VPPINFRA_HEADERS fifo.h file.h format.h + format_ansi.h format_table.h hash.h heap.h @@ -175,6 +203,7 @@ set(VPPINFRA_HEADERS smp.h socket.h sparse_vec.h + stack.h string.h time.h time_range.h @@ -229,18 +258,9 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") ) endif() -if("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") - option(VPP_USE_EXTERNAL_LIBEXECINFO "Use external libexecinfo (useful for non-glibc targets)." ON) -else() - option(VPP_USE_EXTERNAL_LIBEXECINFO "Use external libexecinfo (useful for non-glibc targets)." OFF) -endif() - -if(VPP_USE_EXTERNAL_LIBEXECINFO) - set(EXECINFO_LIB execinfo) -endif() add_vpp_library(vppinfra SOURCES ${VPPINFRA_SRCS} - LINK_LIBRARIES m ${EXECINFO_LIB} + LINK_LIBRARIES m ${VPPINFRA_LIBS} INSTALL_HEADERS ${VPPINFRA_HEADERS} COMPONENT libvppinfra LTO @@ -265,6 +285,7 @@ if(VPP_BUILD_VPPINFRA_TESTS) longjmp macros maplog + mhash pmalloc pool_alloc pool_iterate diff --git a/src/vppinfra/asm_mips.h b/src/vppinfra/asm_mips.h deleted file mode 100644 index 7c9e69586f4..00000000000 --- a/src/vppinfra/asm_mips.h +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (c) 2015 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - Copyright (c) 2004 Eliot Dresselhaus - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef included_asm_mips_h -#define included_asm_mips_h - -/* Encoding of MIPS instructions. */ -/* Encoding of opcode field (op). */ -#define mips_foreach_opcode \ - _(SPECIAL) _(REGIMM) _(j) _(jal) _(beq) _(bne) _(blez) _(bgtz) \ - _(addi) _(addiu) _(slti) _(sltiu) _(andi) _(ori) _(xori) _(lui) \ - _(COP0) _(COP1) _(COP2) _(COP1X) _(beql) _(bnel) _(blezl) _(bgtzl) \ - _(daddi) _(daddiu) _(ldl) _(ldr) _(SPECIAL2) _(jalx) _(MDMX) _(O37) \ - _(lb) _(lh) _(lwl) _(lw) _(lbu) _(lhu) _(lwr) _(lwu) \ - _(sb) _(sh) _(swl) _(sw) _(sdl) _(sdr) _(swr) _(cache) \ - _(ll) _(lwc1) _(lwc2) _(pref) _(lld) _(ldc1) _(ldc2) _(ld) \ - _(sc) _(swc1) _(swc2) _(o73) _(scd) _(sdc1) _(sdc2) _(sd) - -/* Encoding of funct field. */ -#define mips_foreach_special_funct \ - _(sll) _(MOVCI) _(srl) _(sra) _(sllv) _(o05) _(srlv) _(srav) \ - _(jr) _(jalr) _(movz) _(movn) _(syscall) _(break) _(o16) _(sync) \ - _(mfhi) _(mthi) _(mflo) _(mtlo) _(dsllv) _(o25) _(dsrlv) _(dsrav) \ - _(mult) _(multu) _(div) _(divu) _(dmult) _(dmultu) _(ddiv) _(ddivu) \ - _(add) _(addu) _(sub) _(subu) _(and) _(or) _(xor) _(nor) \ - _(o50) _(o51) _(slt) _(sltu) _(dadd) _(daddu) _(dsub) _(dsubu) \ - _(tge) _(tgeu) _(tlt) _(tltu) _(teq) _(o65) _(tne) _(o67) \ - _(dsll) _(o71) _(dsrl) _(dsra) _(dsll32) _(o75) _(dsrl32) _(dsra32) - -/* SPECIAL2 encoding of funct field. */ -#define mips_foreach_special2_funct \ - _(madd) _(maddu) _(mul) _(o03) _(msub) _(msubu) _(o06) _(o07) \ - _(o10) _(o11) _(o12) _(o13) _(o14) _(o15) _(o16) _(o17) \ - _(o20) _(o21) _(o22) _(o23) _(o24) _(o25) _(o26) _(o27) \ - _(o30) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(o37) \ - _(clz) _(clo) _(o42) _(o43) _(dclz) _(dclo) _(o46) _(o47) \ - _(o50) _(o51) _(o52) _(o53) _(o54) _(o55) _(o56) _(o57) \ - _(o60) _(o61) _(o62) _(o63) _(o64) _(o65) _(o66) _(o67) \ - _(o70) _(o71) _(o72) _(o73) _(o74) _(o75) _(o76) _(sdbbp) - -/* REGIMM encoding of rt field. */ -#define mips_foreach_regimm_rt \ - _(bltz) _(bgez) _(bltzl) _(bgezl) _(o04) _(o05) _(o06) _(o07) \ - _(tgei) _(tgeiu) _(tltiu) _(teqi) _(o14) _(tnei) _(o16) _(o17) \ - _(bltzal) _(bgezal) _(bltzall) _(bgezall) _(o24) _(o25) _(o26) _(o27) \ - _(o30) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(o37) - -/* COP0 encoding of rs field. */ -#define mips_foreach_cop0_rs \ - _(mfc0) _(dmfc0) _(o02) _(o03) _(mtc0) _(dmtc0) _(o06) _(o07) \ - _(o10) _(o11) _(o12) _(o13) _(o14) _(o15) _(o16) _(o17) \ - _(C0) _(o21) _(o22) _(o23) _(o24) _(o25) _(o26) _(o27) \ - _(o30) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(o37) - -/* COP0 encoding of funct when rs == RS_CO */ -#define mips_foreach_cop0_funct \ - _(o00) _(tlbr) _(tlbwi) _(o03) _(o04) _(o05) _(tlbwr) _(o07) \ - _(tlbp) _(o11) _(o12) _(o13) _(o14) _(o15) _(o16) _(o17) \ - _(o20) _(o21) _(o22) _(o23) _(o24) _(o25) _(o26) _(o27) \ - _(eret) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(deret) \ - _(wait) _(o41) _(o42) _(o43) _(o44) _(o45) _(o46) _(o47) \ - _(o50) _(o51) _(o52) _(o53) _(o54) _(o55) _(o56) _(o57) \ - _(o60) _(o61) _(o62) _(o63) _(o64) _(o65) _(o66) _(o67) \ - _(o70) _(o71) _(o72) _(o73) _(o74) _(o75) _(o76) _(o77) - -/* COP1 encoding of rs field. */ -#define mips_foreach_cop1_rs \ - _(mfc1) _(dmfc1) _(cfc1) _(o03) _(mtc1) _(dmtc1) _(ctc1) _(o07) \ - _(BC1) _(o11) _(o12) _(o13) _(o14) _(o15) _(o16) _(o17) \ - _(S) _(D) _(o22) _(o23) _(W) _(L) _(o26) _(o27) \ - _(o30) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(o37) - -/* COP1 encoding of funct for S and D */ -#define mips_foreach_cop1_funct \ - _(add) _(sub) _(mul) _(div) _(sqrt) _(abs) _(mov) _(neg) \ - _(roundl) _(truncl) _(ceill) _(floorl) _(roundw) _(truncw) _(ceilw) _(floorw) \ - _(o20) _(MOVCF) _(movz) _(movn) _(o24) _(recip) _(rsqrt) _(o27) \ - _(o30) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(o37) \ - _(cvts) _(cvtd) _(o42) _(o43) _(cvtw) _(cvtl) _(o46) _(o47) \ - _(o50) _(o51) _(o52) _(o53) _(o54) _(o55) _(o56) _(o57) \ - _(cf) _(cun) _(ceq) _(cueq) _(colt) _(cult) _(cole) _(cule) \ - _(csf) _(cngle) _(cseq) _(cngl) _(clt) _(cnge) _(cle) _(cngt) - -/* COP1X encoding of funct */ -#define mips_foreach_cop1x_funct \ - _(lwxc1) _(ldxc1) _(o02) _(o03) _(o04) _(luxc1) _(o06) _(o07) \ - _(swxc1) _(sdxc1) _(o12) _(o13) _(o14) _(suxc1) _(o16) _(prefx) \ - _(o20) _(o21) _(o22) _(o23) _(o24) _(o25) _(o26) _(o27) \ - _(o30) _(o31) _(o32) _(o33) _(o34) _(o35) _(o36) _(o37) \ - _(madds) _(maddd) _(o42) _(o43) _(o44) _(o45) _(o46) _(o47) \ - _(msubs) _(msubd) _(o52) _(o53) _(o54) _(o55) _(o56) _(o57) \ - _(nmadds) _(nmaddd) _(o62) _(o63) _(o64) _(o65) _(o66) _(o67) \ - _(nmsubs) _(nmsubd) _(o72) _(o73) _(o74) _(o75) _(o76) _(o77) - -#define mips_foreach_mdmx_funct \ - _(msgn) _(ceq) _(pickf) _(pickt) _(clt) _(cle) _(min) _(max) \ - _(o10) _(o11) _(sub) _(add) _(and) _(xor) _(or) _(nor) \ - _(sll) _(o21) _(srl) _(sra) _(o24) _(o25) _(o26) _(o27) \ - _(alniob) _(alnvob) _(alniqh) _(alnvqh) _(o34) _(o35) _(o36) _(shfl) \ - _(rzu) _(rnau) _(rneu) _(o43) _(rzs) _(rnas) _(rnes) _(o47) \ - _(o50) _(o51) _(o52) _(o53) _(o54) _(o55) _(o56) _(o57) \ - _(mul) _(o61) _(muls) _(mula) _(o64) _(o65) _(suba) _(adda) \ - _(o70) _(o71) _(o72) _(o73) _(o74) _(o75) _(wac) _(rac) - -#define _(f) MIPS_OPCODE_##f, -typedef enum -{ - mips_foreach_opcode -} mips_insn_opcode_t; -#undef _ - -#define _(f) MIPS_SPECIAL_FUNCT_##f, -typedef enum -{ - mips_foreach_special_funct -} mips_insn_special_funct_t; -#undef _ - -#define _(f) MIPS_SPECIAL2_FUNCT_##f, -typedef enum -{ - mips_foreach_special2_funct -} mips_insn_special2_funct_t; -#undef _ - -#define _(f) MIPS_REGIMM_RT_##f, -typedef enum -{ - mips_foreach_regimm_rt -} mips_insn_regimm_rt_t; -#undef _ - -#define _(f) MIPS_COP0_RS_##f, -typedef enum -{ - mips_foreach_cop0_rs -} mips_insn_cop0_rs_t; -#undef _ - -#define _(f) MIPS_COP0_FUNCT_##f, -typedef enum -{ - mips_foreach_cop0_funct -} mips_insn_cop0_funct_t; -#undef _ - -#define _(f) MIPS_COP1_RS_##f, -typedef enum -{ - mips_foreach_cop1_rs -} mips_insn_cop1_rs_t; -#undef _ - -#define _(f) MIPS_COP1_FUNCT_##f, -typedef enum -{ - mips_foreach_cop1_funct -} mips_insn_cop1_funct_t; -#undef _ - -#define _(f) MIPS_COP1X_FUNCT_##f, -typedef enum -{ - mips_foreach_cop1x_funct -} mips_insn_cop1x_funct_t; -#undef _ - -#define _(f) MIPS_MDMX_FUNCT_##f, -typedef enum -{ - mips_foreach_mdmx_funct -} mips_insn_mdmx_funct_t; -#undef _ - -always_inline mips_insn_opcode_t -mips_insn_get_op (u32 insn) -{ - return (insn >> 26) & 0x3f; -} - -always_inline u32 -mips_insn_get_rs (u32 insn) -{ - return (insn >> 21) & 0x1f; -} - -always_inline u32 -mips_insn_get_rt (u32 insn) -{ - return (insn >> 16) & 0x1f; -} - -always_inline u32 -mips_insn_get_rd (u32 insn) -{ - return (insn >> 11) & 0x1f; -} - -always_inline u32 -mips_insn_get_sa (u32 insn) -{ - return (insn >> 6) & 0x1f; -} - -always_inline u32 -mips_insn_get_funct (u32 insn) -{ - return (insn >> 0) & 0x3f; -} - -always_inline i32 -mips_insn_get_immediate (u32 insn) -{ - return (((i32) insn) << 16) >> 16; -} - -always_inline u32 -mips_insn_encode_i_type (int op, int rs, int rt, int immediate) -{ - u32 insn; - insn = immediate; - insn |= rt << 16; - insn |= rs << 21; - insn |= op << 26; - - ASSERT (mips_insn_get_immediate (insn) == immediate); - ASSERT (mips_insn_get_rt (insn) == rt); - ASSERT (mips_insn_get_rs (insn) == rt); - ASSERT (mips_insn_get_op (insn) == op); - - return insn; -} - -always_inline u32 -mips_insn_encode_j_type (int op, u32 addr) -{ - u32 insn; - - insn = (addr & ((1 << 28) - 1)) / 4; - insn |= op << 26; - - return insn; -} - -always_inline u32 -mips_insn_encode_r_type (int op, int rs, int rt, int rd, int sa, int funct) -{ - u32 insn; - insn = funct; - insn |= sa << 6; - insn |= rd << 11; - insn |= rt << 16; - insn |= rs << 21; - insn |= op << 26; - - ASSERT (mips_insn_get_funct (insn) == funct); - ASSERT (mips_insn_get_sa (insn) == sa); - ASSERT (mips_insn_get_rd (insn) == rd); - ASSERT (mips_insn_get_rt (insn) == rt); - ASSERT (mips_insn_get_rs (insn) == rt); - ASSERT (mips_insn_get_op (insn) == op); - - return insn; -} - -#define mips_insn_r(op,funct,rd,rs,rt,sa) \ - mips_insn_encode_r_type (MIPS_OPCODE_##op, \ - (rs), (rt), (rd), (sa), \ - MIPS_##op##_FUNCT_##funct) - -#define mips_insn_i(op,rs,rt,imm) \ - mips_insn_encode_i_type (MIPS_OPCODE_##op, (rs), (rt), (imm)) - -#define mips_insn_j(op,target) \ - mips_insn_encode_i_type (MIPS_OPCODE_##op, (rs), (rt), (imm)) - -/* Generate unsigned load instructions of data of various sizes. */ -always_inline u32 -mips_insn_load (u32 rd, i32 offset, u32 base, u32 log2_bytes) -{ - int op; - - ASSERT (log2_bytes < 4); - switch (log2_bytes) - { - case 0: - op = MIPS_OPCODE_lbu; - break; - case 1: - op = MIPS_OPCODE_lhu; - break; - case 2: - op = MIPS_OPCODE_lwu; - break; - case 3: - op = MIPS_OPCODE_ld; - break; - } - - return mips_insn_encode_i_type (op, base, rd, offset); -} - -typedef enum -{ - MIPS_REG_SP = 29, - MIPS_REG_RA = 31, -} mips_reg_t; - -#endif /* included_asm_mips_h */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/vppinfra/asm_x86.c b/src/vppinfra/asm_x86.c deleted file mode 100644 index e6e00ce5543..00000000000 --- a/src/vppinfra/asm_x86.c +++ /dev/null @@ -1,1947 +0,0 @@ -/* - * Copyright (c) 2015 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* FIXME - opcode name remove to save table space; enum - x87 - 3dnow - cbw naming -*/ - -#include <vppinfra/error.h> -#include <vppinfra/byte_order.h> -#include <vppinfra/asm_x86.h> - -#define foreach_x86_gp_register \ - _ (AX) _ (CX) _ (DX) _ (BX) \ - _ (SP) _ (BP) _ (SI) _ (DI) - -typedef enum { -#define _(r) X86_INSN_GP_REG_##r, - foreach_x86_gp_register -#undef _ -} x86_insn_gp_register_t; - -typedef union { - struct { - u8 rm : 3; - u8 reg : 3; - u8 mode : 2; - }; - u8 byte; -} x86_insn_modrm_byte_t; - -typedef union { - struct { - u8 base : 3; - u8 index : 3; - u8 log2_scale : 2; - }; - u8 byte; -} x86_insn_sib_byte_t; - -always_inline uword -x86_insn_has_modrm_byte (x86_insn_t * insn) -{ - int i; - for (i = 0; i < ARRAY_LEN (insn->operands); i++) - switch (insn->operands[i].code) - { - case 'G': case 'E': case 'M': case 'R': - return 1; - } - return 0; -} - -always_inline uword -x86_insn_immediate_type (x86_insn_t * insn) -{ - int i; - for (i = 0; i < ARRAY_LEN (insn->operands); i++) - switch (insn->operands[i].code) - { - case 'J': - case 'I': - case 'O': - return insn->operands[i].type; - } - return 0; -} - -/* Opcode extension in modrm byte reg field. */ -#define foreach_x86_insn_modrm_reg_group \ - _ (1) _ (1a) _ (2) _ (3) _ (4) _ (5) _ (6) _ (7) \ - _ (8) _ (9) _ (10) _ (11) _ (12) _ (13) _ (14) \ - _ (15) _ (16) _ (p) - -#define foreach_x86_insn_sse_group \ - _ (10) _ (28) _ (50) _ (58) _ (60) _ (68) _ (70) _ (78) \ - _ (c0) _ (d0) _ (d8) _ (e0) _ (e8) _ (f0) _ (f8) - -enum { -#define _(x) X86_INSN_MODRM_REG_GROUP_##x, - foreach_x86_insn_modrm_reg_group -#undef _ -#define _(x) X86_INSN_SSE_GROUP_##x, - foreach_x86_insn_sse_group -#undef _ -}; - -enum { -#define _(x) \ - X86_INSN_FLAG_MODRM_REG_GROUP_##x \ - = X86_INSN_FLAG_SET_MODRM_REG_GROUP (1 + X86_INSN_MODRM_REG_GROUP_##x), - foreach_x86_insn_modrm_reg_group -#undef _ - -#define _(x) \ - X86_INSN_FLAG_SSE_GROUP_##x \ - = X86_INSN_FLAG_SET_SSE_GROUP (1 + X86_INSN_SSE_GROUP_##x), - foreach_x86_insn_sse_group -#undef _ -}; - -#define foreach_x86_gp_reg \ - _ (AX) _ (CX) _ (DX) _ (BX) \ - _ (SP) _ (BP) _ (SI) _ (DI) - -#define foreach_x86_condition \ - _ (o) _ (no) _ (b) _ (nb) \ - _ (z) _ (nz) _ (be) _ (nbe) \ - _ (s) _ (ns) _ (p) _ (np) \ - _ (l) _ (nl) _ (le) _ (nle) - -#define _3f(x,f,o0,o1,o2) \ -{ \ - .name = #x, \ - .flags = (f), \ - .operands[0] = { .data = #o0 }, \ - .operands[1] = { .data = #o1 }, \ - .operands[2] = { .data = #o2 }, \ -} - -#define _2f(x,f,o0,o1) _3f(x,f,o0,o1,__) -#define _1f(x,f,o0) _2f(x,f,o0,__) -#define _0f(x,f) _1f(x,f,__) - -#define _3(x,o0,o1,o2) _3f(x,0,o0,o1,o2) -#define _2(x,o0,o1) _2f(x,0,o0,o1) -#define _1(x,o0) _1f(x,0,o0) -#define _0(x) _0f(x,0) - -static x86_insn_t x86_insns_one_byte[256] = { - -#define _(x) \ - _2 (x, Eb, Gb), \ - _2 (x, Ev, Gv), \ - _2 (x, Gb, Eb), \ - _2 (x, Gv, Ev), \ - _2 (x, AL, Ib), \ - _2 (x, AX, Iz) - - /* 0x00 */ - _ (add), - _0 (push_es), - _0 (pop_es), - _ (or), - _0 (push_cs), - _0 (escape_two_byte), - - /* 0x10 */ - _ (adc), - _0 (push_ss), - _0 (pop_ss), - _ (sbb), - _0 (push_ds), - _0 (pop_ds), - - /* 0x20 */ - _ (and), - _0 (segment_es), - _0 (daa), - _ (sub), - _0 (segment_cs), - _0 (das), - - /* 0x30 */ - _ (xor), - _0 (segment_ss), - _0 (aaa), - _ (cmp), - _0 (segment_ds), - _0 (aas), - -#undef _ - - /* 0x40 */ -#define _(r) _1 (inc, r), - foreach_x86_gp_reg -#undef _ -#define _(r) _1 (dec, r), - foreach_x86_gp_reg -#undef _ - - /* 0x50 */ -#define _(r) _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, r), - foreach_x86_gp_reg -#undef _ -#define _(r) _1f (pop, X86_INSN_FLAG_DEFAULT_64_BIT, r), - foreach_x86_gp_reg -#undef _ - - /* 0x60 */ - _0 (pusha), - _0 (popa), - _2 (bound, Gv, Ma), - _2 (movsxd, Gv, Ed), - _0 (segment_fs), - _0 (segment_gs), - _0 (operand_type), - _0 (address_size), - _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, Iz), - _3 (imul, Gv, Ev, Iz), - _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, Ib), - _3 (imul, Gv, Ev, Ib), - _1 (insb, DX), - _1 (insw, DX), - _1 (outsb, DX), - _1 (outsw, DX), - - /* 0x70 */ -#define _(x) _1 (j##x, Jb), - foreach_x86_condition -#undef _ - - /* 0x80 */ - _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Eb, Ib), - _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Ev, Iz), - _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Eb, Ib), - _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Ev, Ib), - _2 (test, Eb, Gb), - _2 (test, Ev, Gv), - _2 (xchg, Eb, Gb), - _2 (xchg, Ev, Gv), - _2 (mov, Eb, Gb), - _2 (mov, Ev, Gv), - _2 (mov, Gb, Eb), - _2 (mov, Gv, Ev), - _2 (mov, Ev, Sw), - _2 (lea, Gv, Ev), - _2 (mov, Sw, Ew), - _1f (modrm_group_1a, X86_INSN_FLAG_MODRM_REG_GROUP_1a, Ev), - - /* 0x90 */ - _0 (nop), - _1 (xchg, CX), - _1 (xchg, DX), - _1 (xchg, BX), - _1 (xchg, SP), - _1 (xchg, BP), - _1 (xchg, SI), - _1 (xchg, DI), - _0 (cbw), - _0 (cwd), - _1 (call, Ap), - _0 (wait), - _0 (pushf), - _0 (popf), - _0 (sahf), - _0 (lahf), - - /* 0xa0 */ - _2 (mov, AL, Ob), - _2 (mov, AX, Ov), - _2 (mov, Ob, AL), - _2 (mov, Ov, AX), - _0 (movsb), - _0 (movsw), - _0 (cmpsb), - _0 (cmpsw), - _2 (test, AL, Ib), - _2 (test, AX, Iz), - _1 (stosb, AL), - _1 (stosw, AX), - _1 (lodsb, AL), - _1 (lodsw, AX), - _1 (scasb, AL), - _1 (scasw, AX), - - /* 0xb0 */ - _2 (mov, AL, Ib), - _2 (mov, CL, Ib), - _2 (mov, DL, Ib), - _2 (mov, BL, Ib), - _2 (mov, AH, Ib), - _2 (mov, CH, Ib), - _2 (mov, DH, Ib), - _2 (mov, BH, Ib), -#define _(r) _2 (mov, r, Iv), - foreach_x86_gp_reg -#undef _ - - /* 0xc0 */ - _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, Ib), - _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, Ib), - _1 (ret, Iw), - _0 (ret), - _2 (les, Gz, Mp), - _2 (lds, Gz, Mp), - _2f (modrm_group_11, X86_INSN_FLAG_MODRM_REG_GROUP_11, Eb, Ib), - _2f (modrm_group_11, X86_INSN_FLAG_MODRM_REG_GROUP_11, Ev, Iz), - _2 (enter, Iw, Ib), - _0 (leave), - _1 (ret, Iw), - _0 (ret), - _0 (int3), - _1 (int, Ib), - _0 (into), - _0 (iret), - - /* 0xd0 */ - _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, 1b), - _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, 1b), - _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, CL), - _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, CL), - _0 (aam), - _0 (aad), - _0 (salc), - _0 (xlat), - /* FIXME x87 */ - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - - /* 0xe0 */ - _1 (loopnz, Jb), - _1 (loopz, Jb), - _1 (loop, Jb), - _1 (jcxz, Jb), - _2 (in, AL, Ib), - _2 (in, AX, Ib), - _2 (out, Ib, AL), - _2 (out, Ib, AX), - _1f (call, X86_INSN_FLAG_DEFAULT_64_BIT, Jz), - _1f ( jmp, X86_INSN_FLAG_DEFAULT_64_BIT, Jz), - _1 (jmp, Ap), - _1 (jmp, Jb), - _2 (in, AL, DX), - _2 (in, AX, DX), - _2 (out, DX, AL), - _2 (out, DX, AX), - - /* 0xf0 */ - _0 (lock), - _0 (int1), - _0 (repne), - _0 (rep), - _0 (hlt), - _0 (cmc), - _0f (modrm_group_3, X86_INSN_FLAG_MODRM_REG_GROUP_3), - _0f (modrm_group_3, X86_INSN_FLAG_MODRM_REG_GROUP_3), - _0 (clc), - _0 (stc), - _0 (cli), - _0 (sti), - _0 (cld), - _0 (std), - _1f (modrm_group_4, X86_INSN_FLAG_MODRM_REG_GROUP_4, Eb), - _0f (modrm_group_5, X86_INSN_FLAG_MODRM_REG_GROUP_5), -}; - -static x86_insn_t x86_insns_two_byte[256] = { - /* 0x00 */ - _0f (modrm_group_6, X86_INSN_FLAG_MODRM_REG_GROUP_6), - _0f (modrm_group_7, X86_INSN_FLAG_MODRM_REG_GROUP_7), - _2 (lar, Gv, Ew), - _2 (lsl, Gv, Ew), - _0 (bad), - _0 (syscall), - _0 (clts), - _0 (sysret), - _0 (invd), - _0 (wbinvd), - _0 (bad), - _0 (ud2), - _0 (bad), - _0f (modrm_group_p, X86_INSN_FLAG_MODRM_REG_GROUP_p), - _0 (femms), - _0 (escape_3dnow), - - /* 0x10 */ - _2f (movups, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), - _2f (movups, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx), - _2f (movlps, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx), - _2f (movlps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), - _2f (unpcklps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), - _2f (unpckhps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), - _2f (movhps, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx), - _2f (movhps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), - _0f (modrm_group_16, X86_INSN_FLAG_MODRM_REG_GROUP_16), - _0 (nop), - _0 (nop), - _0 (nop), - _0 (nop), - _0 (nop), - _0 (nop), - _0 (nop), - - /* 0x20 */ - _2 (mov, Rv, Cv), - _2 (mov, Rv, Dv), - _2 (mov, Cv, Rv), - _2 (mov, Dv, Rv), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _2f (movaps, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), - _2f (movaps, X86_INSN_FLAG_SSE_GROUP_28, Ex, Gx), - _2f (cvtpi2ps, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), - _2f (movntps, X86_INSN_FLAG_SSE_GROUP_28, Mx, Gx), - _2f (cvttps2pi, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), - _2f (cvtps2pi, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), - _2f (ucomiss, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), - _2f (comiss, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), - - /* 0x30 */ - _0 (wrmsr), - _0 (rdtsc), - _0 (rdmsr), - _0 (rdpmc), - _0 (sysenter), - _0 (sysexit), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - - /* 0x40 */ -#define _(x) _2 (cmov##x, Gv, Ev), - foreach_x86_condition -#undef _ - - /* 0x50 */ - _2f (movmskps, X86_INSN_FLAG_SSE_GROUP_50, Gd, Rx), - _2f (sqrtps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), - _2f (rsqrtps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), - _2f (rcpps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), - _2f (andps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), - _2f (andnps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), - _2f (orps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), - _2f (xorps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), - _2f (addps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), - _2f (mulps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), - _2f (cvtps2pd, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), - _2f (cvtdq2ps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), - _2f (subps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), - _2f (minps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), - _2f (divps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), - _2f (maxps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), - - /* 0x60 */ - _2f (punpcklbw, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), - _2f (punpcklwd, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), - _2f (punpckldq, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), - _2f (packsswb, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), - _2f (pcmpgtb, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), - _2f (pcmpgtw, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), - _2f (pcmpgtd, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), - _2f (packuswb, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), - _2f (punpckhbw, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em), - _2f (punpckhwd, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em), - _2f (punpckhdq, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em), - _2f (packssdw, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em), - _0f (bad, X86_INSN_FLAG_SSE_GROUP_68), - _0f (bad, X86_INSN_FLAG_SSE_GROUP_68), - _2f (movd, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em), - _2f (movq, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em), - - /* 0x70 */ - _3f (pshufw, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em, Ib), - _0f (modrm_group_12, X86_INSN_FLAG_MODRM_REG_GROUP_12), - _0f (modrm_group_13, X86_INSN_FLAG_MODRM_REG_GROUP_13), - _0f (modrm_group_14, X86_INSN_FLAG_MODRM_REG_GROUP_14), - _2f (pcmpeqb, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em), - _2f (pcmpeqw, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em), - _2f (pcmpeqd, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em), - _0f (emms, X86_INSN_FLAG_SSE_GROUP_70), - _0f (bad, X86_INSN_FLAG_SSE_GROUP_78), - _0f (bad, X86_INSN_FLAG_SSE_GROUP_78), - _0f (bad, X86_INSN_FLAG_SSE_GROUP_78), - _0f (bad, X86_INSN_FLAG_SSE_GROUP_78), - _0f (bad, X86_INSN_FLAG_SSE_GROUP_78), - _0f (bad, X86_INSN_FLAG_SSE_GROUP_78), - _2f (movd, X86_INSN_FLAG_SSE_GROUP_78, Em, Gm), - _2f (movq, X86_INSN_FLAG_SSE_GROUP_78, Em, Gm), - - /* 0x80 */ -#define _(x) _1 (jmp##x, Jz), - foreach_x86_condition -#undef _ - - /* 0x90 */ -#define _(x) _1 (set##x, Eb), - foreach_x86_condition -#undef _ - - /* 0xa0 */ - _0 (push_fs), - _0 (pop_fs), - _0 (cpuid), - _2 (bt, Ev, Gv), - _3 (shld, Ev, Gv, Ib), - _3 (shld, Ev, Gv, CL), - _0 (bad), - _0 (bad), - _0 (push_gs), - _0 (pop_gs), - _0 (rsm), - _2 (bts, Ev, Gv), - _3 (shrd, Ev, Gv, Ib), - _3 (shrd, Ev, Gv, CL), - _0f (modrm_group_15, X86_INSN_FLAG_MODRM_REG_GROUP_15), - _2 (imul, Gv, Ev), - - /* 0xb0 */ - _2 (cmpxchg, Eb, Gb), - _2 (cmpxchg, Ev, Gv), - _2 (lss, Gz, Mp), - _2 (btr, Ev, Gv), - _2 (lfs, Gz, Mp), - _2 (lgs, Gz, Mp), - _2 (movzbl, Gv, Eb), - _2 (movzwl, Gv, Ew), - _0 (bad), - _0f (modrm_group_10, X86_INSN_FLAG_MODRM_REG_GROUP_10), - _2f (modrm_group_8, X86_INSN_FLAG_MODRM_REG_GROUP_8, Ev, Ib), - _2 (btc, Ev, Gv), - _2 (bsf, Gv, Ev), - _2 (bsr, Gv, Ev), - _2 (movsx, Gv, Eb), - _2 (movsx, Gv, Ew), - - /* 0xc0 */ - _2 (xadd, Eb, Gb), - _2 (xadd, Ev, Gv), - _3f (cmpps, X86_INSN_FLAG_SSE_GROUP_c0, Gx, Ex, Ib), - _2 (movnti, Mv, Gv), - _3f (pinsrw, X86_INSN_FLAG_SSE_GROUP_c0, Gm, Ew, Ib), - _3f (pextrw, X86_INSN_FLAG_SSE_GROUP_c0, Gd, Rm, Ib), - _3f (shufps, X86_INSN_FLAG_SSE_GROUP_c0, Gx, Ex, Ib), - _1f (modrm_group_9, X86_INSN_FLAG_MODRM_REG_GROUP_9, Mx), -#define _(r) _1 (bswap, r), - foreach_x86_gp_reg -#undef _ - - /* 0xd0 */ - _0f (bad, X86_INSN_FLAG_SSE_GROUP_d0), - _2f (psrlw, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em), - _2f (psrld, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em), - _2f (psrlq, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em), - _2f (paddq, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em), - _2f (pmullw, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em), - _0f (bad, X86_INSN_FLAG_SSE_GROUP_d0), - _2f (pmovmskb, X86_INSN_FLAG_SSE_GROUP_d0, Gd, Rm), - _2f (psubusb, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), - _2f (psubusw, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), - _2f (pminub, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), - _2f (pand, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), - _2f (paddusb, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), - _2f (paddusw, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), - _2f (pmaxub, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), - _2f (pandn, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), - - /* 0xe0 */ - _2f (pavgb, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), - _2f (psraw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), - _2f (psrad, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), - _2f (pavgw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), - _2f (pmulhuw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), - _2f (pmulhw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), - _2f (bad, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), - _2f (movntq, X86_INSN_FLAG_SSE_GROUP_e0, Mm, Gm), - _2f (psubsb, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), - _2f (psubsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), - _2f (pminsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), - _2f (por, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), - _2f (paddsb, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), - _2f (paddsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), - _2f (pmaxsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), - _2f (pxor, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), - - /* 0xf0 */ - _0f (bad, X86_INSN_FLAG_SSE_GROUP_f0), - _2f (psllw, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), - _2f (pslld, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), - _2f (psllq, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), - _2f (pmuludq, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), - _2f (pmaddwd, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), - _2f (psadbw, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), - _2f (maskmovq, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), - _2f (psubb, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), - _2f (psubw, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), - _2f (psubd, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), - _2f (psubq, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), - _2f (paddb, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), - _2f (paddw, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), - _2f (paddd, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), - _0f (bad, X86_INSN_FLAG_SSE_GROUP_f8), -}; - -typedef struct { - x86_insn_t insns[8]; -} x86_insn_group8_t; - -/* Escape groups are indexed by modrm reg field. */ -static x86_insn_group8_t x86_insn_modrm_reg_groups[] = { - [X86_INSN_MODRM_REG_GROUP_1].insns = { - _0 (add), _0 ( or), _0 (adc), _0 (sbb), - _0 (and), _0 (sub), _0 (xor), _0 (cmp), - }, - - [X86_INSN_MODRM_REG_GROUP_1a].insns = { - _0f (pop, X86_INSN_FLAG_DEFAULT_64_BIT), - _0 (bad), _0 (bad), _0 (bad), - _0 (bad), _0 (bad), _0 (bad), _0 (bad), - }, - - [X86_INSN_MODRM_REG_GROUP_2].insns = { - _0 (rol), _0 (ror), _0 (rcl), _0 (rcr), - _0 (shl), _0 (shr), _0 (sal), _0 (sar), - }, - - [X86_INSN_MODRM_REG_GROUP_3].insns = { - _0 (test), _0 (test), _0 (not), _0 (neg), - _0 (mul), _0 (imul), _0 (div), _0 (idiv), - }, - - [X86_INSN_MODRM_REG_GROUP_4].insns = { - _0 (inc), _0 (dec), _0 (bad), _0 (bad), - _0 (bad), _0 (bad), _0 (bad), _0 (bad), - }, - - [X86_INSN_MODRM_REG_GROUP_5].insns = { - _1 (inc, Ev), - _1 (dec, Ev), - _1f (call, X86_INSN_FLAG_DEFAULT_64_BIT, Ev), - _1 (call, Mp), - _1f (jmp, X86_INSN_FLAG_DEFAULT_64_BIT, Ev), - _1 (jmp, Mp), - _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, Ev), - _0 (bad), - }, - - [X86_INSN_MODRM_REG_GROUP_6].insns = { - _1 (sldt, Ev), - _1 (str, Ev), - _1 (lldt, Ev), - _1 (ltr, Ev), - _1 (verr, Ev), - _1 (verw, Ev), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_MODRM_REG_GROUP_7].insns = { - _1 (sgdt, Mv), - _1 (sidt, Mv), - _1 (lgdt, Mv), - _1 (lidt, Mv), - _1 (smsw, Ev), - _0 (bad), - _1 (lmsw, Ew), - _1 (invlpg, Mv), - }, - - [X86_INSN_MODRM_REG_GROUP_8].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _2 (bt, Ev, Ib), - _2 (bts, Ev, Ib), - _2 (btr, Ev, Ib), - _2 (btc, Ev, Ib), - }, - - [X86_INSN_MODRM_REG_GROUP_9].insns = { - _0 (bad), - _1 (cmpxchg, Mx), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_MODRM_REG_GROUP_10].insns = { - _0 (bad), _0 (bad), _0 (bad), _0 (bad), - _0 (bad), _0 (bad), _0 (bad), _0 (bad), - }, - - [X86_INSN_MODRM_REG_GROUP_11].insns = { - _0 (mov), _0 (bad), _0 (bad), _0 (bad), - _0 (bad), _0 (bad), _0 (bad), _0 (bad), - }, - - [X86_INSN_MODRM_REG_GROUP_12].insns = { - _0 (bad), - _0 (bad), - _2 (psrlw, Rm, Ib), - _0 (bad), - _2 (psraw, Rm, Ib), - _0 (bad), - _2 (psllw, Rm, Ib), - _0 (bad), - }, - - [X86_INSN_MODRM_REG_GROUP_13].insns = { - _0 (bad), - _0 (bad), - _2 (psrld, Rm, Ib), - _0 (bad), - _2 (psrad, Rm, Ib), - _0 (bad), - _2 (pslld, Rm, Ib), - _0 (bad), - }, - - [X86_INSN_MODRM_REG_GROUP_14].insns = { - _0 (bad), - _0 (bad), - _2 (psrlq, Rm, Ib), - _0f (bad, 0), - _0 (bad), - _0 (bad), - _2 (psllq, Rm, Ib), - _0f (bad, 0), - }, - - [X86_INSN_MODRM_REG_GROUP_15].insns = { - _1 (fxsave, Mv), - _1 (fxrstor, Mv), - _1 (ldmxcsr, Mv), - _1 (stmxcsr, Mv), - _0 (bad), - _1 (lfence, Mv), - _1 (mfence, Mv), - _1 (sfence, Mv), - }, - - [X86_INSN_MODRM_REG_GROUP_16].insns = { - _1 (prefetch_nta, Mv), - _1 (prefetch_t0, Mv), - _1 (prefetch_t1, Mv), - _1 (prefetch_t2, Mv), - _1 (prefetch_nop, Mv), - _1 (prefetch_nop, Mv), - _1 (prefetch_nop, Mv), - _1 (prefetch_nop, Mv), - }, - - [X86_INSN_MODRM_REG_GROUP_p].insns = { - _1 (prefetch_exclusive, Mv), - _1 (prefetch_modified, Mv), - _1 (prefetch_nop, Mv), - _1 (prefetch_modified, Mv), - _1 (prefetch_nop, Mv), - _1 (prefetch_nop, Mv), - _1 (prefetch_nop, Mv), - _1 (prefetch_nop, Mv), - }, -}; - -static x86_insn_group8_t x86_insn_sse_groups_repz[] = { - [X86_INSN_SSE_GROUP_10].insns = { - _2 (movss, Gx, Ex), - _2 (movss, Ex, Gx), - _2 (movsldup, Gx, Ex), - _0 (bad), - _0 (bad), - _0 (bad), - _2 (movshdup, Gx, Ex), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_28].insns = { - _0 (bad), - _0 (bad), - _2 (cvtsi2ss, Gx, Ev), - _0 (bad), - _2 (cvttss2si, Gv, Ex), - _2 (cvtss2si, Gv, Ex), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_50].insns = { - _0 (bad), - _2 (sqrtss, Gx, Ex), - _2 (rsqrtps, Gx, Ex), - _2 (rcpss, Gx, Ex), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_58].insns = { - _2 (addss, Gx, Ex), - _2 (mulss, Gx, Ex), - _2 (cvtss2sd, Gx, Ex), - _2 (cvttps2dq, Gx, Ex), - _2 (subss, Gx, Ex), - _2 (minss, Gx, Ex), - _2 (divss, Gx, Ex), - _2 (maxss, Gx, Ex), - }, - - [X86_INSN_SSE_GROUP_60].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_68].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _2 (movdqu, Gx, Ex), - }, - - [X86_INSN_SSE_GROUP_70].insns = { - _3 (pshufhw, Gx, Ex, Ib), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_78].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _2 (movq, Gx, Ex), - _2 (movdqu, Ex, Gx), - }, - - [X86_INSN_SSE_GROUP_c0].insns = { - _0 (bad), - _0 (bad), - _3 (cmpss, Gx, Ex, Ib), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_d0].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _2 (movq2dq, Gx, Em), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_d8].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_e0].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _2 (cvtdq2pd, Gx, Ex), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_e8].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_f0].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_f8].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, -}; - -static x86_insn_group8_t x86_insn_sse_groups_operand_size[] = { - [X86_INSN_SSE_GROUP_10].insns = { - _2 (movupd, Gx, Ex), - _2 (movupd, Ex, Gx), - _2 (movlpd, Gx, Ex), - _2 (movlpd, Ex, Gx), - _2 (unpcklpd, Gx, Ex), - _2 (unpckhpd, Gx, Ex), - _2 (movhpd, Gx, Mx), - _2 (movhpd, Mx, Gx), - }, - - [X86_INSN_SSE_GROUP_28].insns = { - _2 (movapd, Gx, Ex), - _2 (movapd, Ex, Gx), - _2 (cvtpi2pd, Gx, Ex), - _2 (movntpd, Mx, Gx), - _2 (cvttpd2pi, Gx, Mx), - _2 (cvtpd2pi, Gx, Mx), - _2 (ucomisd, Gx, Ex), - _2 (comisd, Gx, Ex), - }, - - [X86_INSN_SSE_GROUP_50].insns = { - _2 (movmskpd, Gd, Rx), - _2 (sqrtpd, Gx, Ex), - _0 (bad), - _0 (bad), - _2 (andpd, Gx, Ex), - _2 (andnpd, Gx, Ex), - _2 (orpd, Gx, Ex), - _2 (xorpd, Gx, Ex), - }, - - [X86_INSN_SSE_GROUP_58].insns = { - _2 (addpd, Gx, Ex), - _2 (mulpd, Gx, Ex), - _2 (cvtpd2ps, Gx, Ex), - _2 (cvtps2dq, Gx, Ex), - _2 (subpd, Gx, Ex), - _2 (minpd, Gx, Ex), - _2 (divpd, Gx, Ex), - _2 (maxpd, Gx, Ex), - }, - - [X86_INSN_SSE_GROUP_60].insns = { - _2 (punpcklbw, Gx, Ex), - _2 (punpcklwd, Gx, Ex), - _2 (punpckldq, Gx, Ex), - _2 (packsswb, Gx, Ex), - _2 (pcmpgtb, Gx, Ex), - _2 (pcmpgtw, Gx, Ex), - _2 (pcmpgtd, Gx, Ex), - _2 (packuswb, Gx, Ex), - }, - - [X86_INSN_SSE_GROUP_68].insns = { - _2 (punpckhbw, Gx, Ex), - _2 (punpckhwd, Gx, Ex), - _2 (punpckhdq, Gx, Ex), - _2 (packssdw, Gx, Ex), - _2 (punpcklqdq, Gx, Ex), - _2 (punpckhqdq, Gx, Ex), - _2 (movd, Gx, Ev), - _2 (movdqa, Gx, Ex), - }, - - [X86_INSN_SSE_GROUP_70].insns = { - _3 (pshufd, Gx, Ex, Ib), - _0f (modrm_group_12, X86_INSN_FLAG_MODRM_REG_GROUP_12), - _0f (modrm_group_13, X86_INSN_FLAG_MODRM_REG_GROUP_13), - _0f (modrm_group_14, X86_INSN_FLAG_MODRM_REG_GROUP_14), - _2 (pcmpeqb, Gx, Ex), - _2 (pcmpeqw, Gx, Ex), - _2 (pcmpeqd, Gx, Ex), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_78].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _2 (haddpd, Gx, Ex), - _2 (hsubpd, Gx, Ex), - _2 (movd, Ev, Gx), - _2 (movdqa, Ex, Gx), - }, - - [X86_INSN_SSE_GROUP_c0].insns = { - _0 (bad), - _0 (bad), - _3 (cmppd, Gx, Ex, Ib), - _0 (bad), - _3 (pinsrw, Gx, Ew, Ib), - _3 (pextrw, Gd, Gx, Ib), - _3 (shufpd, Gx, Ex, Ib), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_d0].insns = { - _2 (addsubpd, Gx, Ex), - _2 (psrlw, Gx, Ex), - _2 (psrld, Gx, Ex), - _2 (psrlq, Gx, Ex), - _2 (paddq, Gx, Ex), - _2 (pmullw, Gx, Ex), - _2 (movq, Ex, Gx), - _2 (pmovmskb, Gd, Rx), - }, - - [X86_INSN_SSE_GROUP_d8].insns = { - _2 (psubusb, Gx, Ex), - _2 (psubusw, Gx, Ex), - _2 (pminub, Gx, Ex), - _2 (pand, Gx, Ex), - _2 (paddusb, Gx, Ex), - _2 (paddusw, Gx, Ex), - _2 (pmaxub, Gx, Ex), - _2 (pandn, Gx, Ex), - }, - - [X86_INSN_SSE_GROUP_e0].insns = { - _2 (pavgb, Gx, Ex), - _2 (psraw, Gx, Ex), - _2 (psrad, Gx, Ex), - _2 (pavgw, Gx, Ex), - _2 (pmulhuw, Gx, Ex), - _2 (pmulhw, Gx, Ex), - _2 (cvttpd2dq, Gx, Ex), - _2 (movntdq, Mx, Gx), - }, - - [X86_INSN_SSE_GROUP_e8].insns = { - _2 (psubsb, Gx, Ex), - _2 (psubsw, Gx, Ex), - _2 (pminsw, Gx, Ex), - _2 (por, Gx, Ex), - _2 (paddsb, Gx, Ex), - _2 (paddsw, Gx, Ex), - _2 (pmaxsw, Gx, Ex), - _2 (pxor, Gx, Ex), - }, - - [X86_INSN_SSE_GROUP_f0].insns = { - _0 (bad), - _2 (psllw, Gx, Ex), - _2 (pslld, Gx, Ex), - _2 (psllq, Gx, Ex), - _2 (pmuludq, Gx, Ex), - _2 (pmaddwd, Gx, Ex), - _2 (psadbw, Gx, Ex), - _2 (maskmovdqu, Gx, Ex), - }, - - [X86_INSN_SSE_GROUP_f8].insns = { - _2 (psubb, Gx, Ex), - _2 (psubw, Gx, Ex), - _2 (psubd, Gx, Ex), - _2 (psubq, Gx, Ex), - _2 (paddb, Gx, Ex), - _2 (paddw, Gx, Ex), - _2 (paddd, Gx, Ex), - _0 (bad), - }, -}; - -static x86_insn_group8_t x86_insn_sse_groups_repnz[] = { - [X86_INSN_SSE_GROUP_10].insns = { - _2 (movsd, Gx, Ex), - _2 (movsd, Ex, Gx), - _2 (movddup, Gx, Ex), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_28].insns = { - _0 (bad), - _0 (bad), - _2 (cvtsi2sd, Gx, Ev), - _0 (bad), - _2 (cvttsd2si, Gv, Ex), - _2 (cvtsd2si, Gv, Ex), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_50].insns = { - _0 (bad), - _2 (sqrtsd, Gx, Ex), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_58].insns = { - _2 (addsd, Gx, Ex), - _2 (mulsd, Gx, Ex), - _2 (cvtsd2ss, Gx, Ex), - _0 (bad), - _2 (subsd, Gx, Ex), - _2 (minsd, Gx, Ex), - _2 (divsd, Gx, Ex), - _2 (maxsd, Gx, Ex), - }, - - [X86_INSN_SSE_GROUP_60].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_68].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_70].insns = { - _3 (pshuflw, Gx, Ex, Ib), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_78].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _2 (haddps, Gx, Ex), - _2 (hsubps, Gx, Ex), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_c0].insns = { - _0 (bad), - _0 (bad), - _3 (cmpsd, Gx, Ex, Ib), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_d0].insns = { - _2 (addsubps, Gx, Ex), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _2 (movdq2q, Gm, Ex), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_d8].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_e0].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _2 (cvtpd2dq, Gx, Ex), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_e8].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_f0].insns = { - _2 (lddqu, Gx, Mx), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, - - [X86_INSN_SSE_GROUP_f8].insns = { - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - _0 (bad), - }, -}; - -#undef _ - -/* Parses memory displacements and immediates. */ -static u8 * x86_insn_parse_number (u32 log2_n_bytes, - u8 * code, u8 * code_end, - i64 * result) -{ - i64 x = 0; - - if (code + (1 << log2_n_bytes) > code_end) - return 0; - - switch (log2_n_bytes) - { - case 3: - x = clib_little_to_host_unaligned_mem_u64 ((u64 *) code); - break; - - case 2: - x = (i32) clib_little_to_host_unaligned_mem_u32 ((u32 *) code); - break; - - case 1: - x = (i16) clib_little_to_host_unaligned_mem_u16 ((u16 *) code); - break; - - case 0: - x = (i8) code[0]; - break; - - default: - ASSERT (0); - } - - *result = x; - return code + (1 << log2_n_bytes); -} - -static u32 -x86_insn_log2_immediate_bytes (x86_insn_parse_t * p, x86_insn_t * insn) -{ - u32 i = ~0; - switch (x86_insn_immediate_type (insn)) - { - case 'b': i = 0; break; - case 'w': i = 1; break; - case 'd': i = 2; break; - case 'q': i = 3; break; - - case 'z': - i = p->log2_effective_operand_bytes; - if (i > 2) i = 2; - break; - - case 'v': - i = p->log2_effective_operand_bytes; - break; - - default: - i = ~0; - break; - } - - return i; -} - -static u8 * -x86_insn_parse_modrm_byte (x86_insn_parse_t * x, - x86_insn_modrm_byte_t modrm, - u32 parse_flags, - u8 * code, - u8 * code_end) -{ - u8 effective_address_bits; - - if (parse_flags & X86_INSN_PARSE_64_BIT) - effective_address_bits = (x->flags & X86_INSN_ADDRESS_SIZE) ? 32 : 64; - else if (parse_flags & X86_INSN_PARSE_32_BIT) - effective_address_bits = (x->flags & X86_INSN_ADDRESS_SIZE) ? 16 : 32; - else - effective_address_bits = (x->flags & X86_INSN_ADDRESS_SIZE) ? 32 : 16; - - x->log2_effective_address_bytes = 1; - x->log2_effective_address_bytes += effective_address_bits > 16; - x->log2_effective_address_bytes += effective_address_bits > 32; - - x->regs[0] |= modrm.reg; - if (modrm.mode == 3) - x->regs[1] |= modrm.rm; - else - { - u32 log2_disp_bytes = ~0; - - x->flags |= X86_INSN_IS_ADDRESS; - - if (effective_address_bits != 16) - { - u8 has_sib_byte = 0; - - switch (modrm.mode) - { - case 0: - /* When base is bp displacement is present for mode 0. */ - if (modrm.rm == X86_INSN_GP_REG_BP) - { - log2_disp_bytes = x->log2_effective_address_bytes; - break; - } - else if (modrm.rm == X86_INSN_GP_REG_SP - && effective_address_bits != 16) - { - has_sib_byte = 1; - break; - } - /* fall through */ - case 1: - case 2: - x->regs[1] |= modrm.rm; - x->flags |= X86_INSN_HAS_BASE; - if (modrm.mode != 0) - { - log2_disp_bytes = (modrm.mode == 1 - ? 0 - : x->log2_effective_address_bytes); - if (log2_disp_bytes > 2) - log2_disp_bytes = 2; - } - break; - } - - if (has_sib_byte) - { - x86_insn_sib_byte_t sib; - - if (code >= code_end) - return 0; - sib.byte = *code++; - - x->log2_index_scale = 1 << sib.log2_scale; - x->regs[1] |= sib.base; - x->flags |= X86_INSN_HAS_BASE; - - if (sib.index != X86_INSN_GP_REG_SP) - { - x->regs[2] |= sib.index; - x->flags |= X86_INSN_HAS_INDEX; - } - } - } - else - { - /* effective_address_bits == 16 */ - switch (modrm.mode) - { - case 0: - if (modrm.rm == 6) - { - /* [disp16] */ - log2_disp_bytes = 1; - break; - } - /* fall through */ - case 1: - case 2: - switch (modrm.rm) - { - case 0: /* [bx + si/di] */ - case 1: - x->regs[1] = X86_INSN_GP_REG_BX; - x->regs[2] = X86_INSN_GP_REG_SI + (modrm.rm & 1); - x->flags |= X86_INSN_HAS_BASE | X86_INSN_HAS_INDEX; - break; - - case 2: /* [bp + si/di] */ - case 3: - x->regs[1] = X86_INSN_GP_REG_BP; - x->regs[2] = X86_INSN_GP_REG_SI + (modrm.rm & 1); - x->flags |= X86_INSN_HAS_BASE | X86_INSN_HAS_INDEX; - break; - - case 4: /* [si/di] */ - case 5: - x->regs[1] = X86_INSN_GP_REG_SI + (modrm.rm & 1); - x->flags |= X86_INSN_HAS_BASE; - break; - - case 6: /* [bp + disp] */ - x->regs[1] = X86_INSN_GP_REG_BP; - x->flags |= X86_INSN_HAS_BASE; - break; - - case 7: /* [bx + disp] */ - x->regs[1] = X86_INSN_GP_REG_BX; - x->flags |= X86_INSN_HAS_BASE; - break; - } - - if (modrm.mode != 0) - log2_disp_bytes = modrm.mode == 1 ? 0 : 1; - break; - } - } - - if (log2_disp_bytes != ~0) - { - i64 disp; - code = x86_insn_parse_number (log2_disp_bytes, code, code_end, - &disp); - if (code) - x->displacement = disp; - } - } - - return code; -} - -u8 * x86_insn_parse (x86_insn_parse_t * p, u8 * code_start) -{ - u8 i, * code, * code_end; - x86_insn_t * insn, * group_insn; - u8 default_operand_bits, effective_operand_bits; - u32 opcode, parse_flags; - - /* Preserve global parse flags. */ - parse_flags = p->flags & (X86_INSN_PARSE_32_BIT | X86_INSN_PARSE_64_BIT); - clib_memset (p, 0, sizeof (p[0])); - p->flags = parse_flags; - - /* 64 implies 32 bit parsing. */ - if (parse_flags & X86_INSN_PARSE_64_BIT) - parse_flags |= X86_INSN_PARSE_32_BIT; - - /* Instruction must be <= 15 bytes. */ - code = code_start; - code_end = code + 15; - - /* Parse legacy prefixes. */ - while (1) - { - if (code >= code_end) - goto insn_too_long; - i = code[0]; - code++; - switch (i) - { - default: goto prefix_done; - - /* Set flags based on prefix. */ -#define _(x,o) case o: p->flags |= X86_INSN_##x; break; - foreach_x86_legacy_prefix; -#undef _ - } - } - prefix_done: - - /* REX prefix. */ - if ((parse_flags & X86_INSN_PARSE_64_BIT) && i >= 0x40 && i <= 0x4f) - { - p->regs[0] |= ((i & (1 << 2)) != 0) << 3; /* r bit */ - p->regs[1] |= ((i & (1 << 0)) != 0) << 3; /* b bit */ - p->regs[2] |= ((i & (1 << 1)) != 0) << 3; /* x bit */ - p->flags |= ((i & (1 << 3)) /* w bit */ - ? X86_INSN_OPERAND_SIZE_64 : 0); - if (code >= code_end) - goto insn_too_long; - i = *code++; - } - - opcode = i; - if (opcode == 0x0f) - { - /* two byte opcode. */; - if (code >= code_end) - goto insn_too_long; - i = *code++; - opcode = (opcode << 8) | i; - insn = x86_insns_two_byte + i; - } - else - { - static x86_insn_t arpl = { - .name = "arpl", - .operands[0].data = "Ew", - .operands[1].data = "Gw", - }; - - if (PREDICT_FALSE (i == 0x63 - && ! (parse_flags & X86_INSN_PARSE_64_BIT))) - insn = &arpl; - else - insn = x86_insns_one_byte + i; - } - - if ((i = X86_INSN_FLAG_GET_SSE_GROUP (insn->flags)) != 0) - { - x86_insn_group8_t * g8; - - if (p->flags & X86_INSN_OPERAND_SIZE) - g8 = x86_insn_sse_groups_operand_size; - else if (p->flags & X86_INSN_REPZ) - g8 = x86_insn_sse_groups_repz; - else if (p->flags & X86_INSN_REPNZ) - g8 = x86_insn_sse_groups_repnz; - else - g8 = 0; - - /* insn flags have 1 + group so != 0 test above can work. */ - ASSERT ((i - 1) < ARRAY_LEN (x86_insn_sse_groups_operand_size)); - if (g8) - insn = g8[i - 1].insns + (opcode & 7); - } - - /* Parse modrm and displacement if present. */ - if (x86_insn_has_modrm_byte (insn)) - { - x86_insn_modrm_byte_t modrm; - - if (code >= code_end) - goto insn_too_long; - modrm.byte = *code++; - - /* Handle special 0x0f01 and 0x0fae encodings. */ - if (PREDICT_FALSE (modrm.mode == 3 - && (opcode == 0x0f01 - || opcode == 0x0fae))) - { - static x86_insn_t x86_insns_0f01_special[] = { - _0 (swapgs), _0 (rdtscp), _0 (bad), _0 (bad), - _0 (bad), _0 (bad), _0 (bad), _0 (bad), - }; - static x86_insn_t x86_insns_0fae_special[] = { - _0 (vmrun), _0 (vmmcall), _0 (vmload), _0 (vmsave), - _0 (stgi), _0 (clgi), _0 (skinit), _0 (invlpga), - }; - - if (opcode == 0x0f01) - insn = x86_insns_0f01_special; - else - insn = x86_insns_0fae_special; - insn += modrm.rm; - opcode = (opcode << 8) | modrm.byte; - } - else - { - code = x86_insn_parse_modrm_byte (p, modrm, parse_flags, - code, code_end); - if (! code) - goto insn_too_long; - } - } - - group_insn = 0; - if ((i = X86_INSN_FLAG_GET_MODRM_REG_GROUP (insn->flags)) != 0) - { - u32 g = i - 1; - ASSERT (g < ARRAY_LEN (x86_insn_modrm_reg_groups)); - group_insn = x86_insn_modrm_reg_groups[g].insns + (p->regs[0] & 7); - } - - p->insn = insn[0]; - if (group_insn) - { - u32 k; - p->insn.name = group_insn->name; - p->insn.flags |= group_insn->flags; - for (k = 0; k < ARRAY_LEN (group_insn->operands); k++) - if (x86_insn_operand_is_valid (group_insn, k)) - p->insn.operands[k] = group_insn->operands[k]; - } - - default_operand_bits - = ((((parse_flags & X86_INSN_PARSE_32_BIT) != 0) - ^ ((p->flags & X86_INSN_OPERAND_SIZE) != 0)) - ? BITS (u32) : BITS (u16)); - - if ((parse_flags & X86_INSN_PARSE_64_BIT) - && (p->insn.flags & X86_INSN_FLAG_DEFAULT_64_BIT)) - default_operand_bits = BITS (u64); - - effective_operand_bits = default_operand_bits; - if (p->flags & X86_INSN_OPERAND_SIZE_64) - effective_operand_bits = BITS (u64); - - p->log2_effective_operand_bytes = 1; - p->log2_effective_operand_bytes += effective_operand_bits > 16; - p->log2_effective_operand_bytes += effective_operand_bits > 32; - - /* Parse immediate if present. */ - { - u32 l = x86_insn_log2_immediate_bytes (p, insn); - if (l <= 3) - { - code = x86_insn_parse_number (l, code, code_end, &p->immediate); - if (! code) - goto insn_too_long; - } - } - - return code; - - insn_too_long: - return 0; -} - -static u8 * format_x86_gp_reg_operand (u8 * s, va_list * va) -{ - u32 r = va_arg (*va, u32); - u32 log2_n_bytes = va_arg (*va, u32); - - const char names8[8] = "acdbsbsd"; - const char names16[8] = "xxxxppii"; - - ASSERT (r < 16); - - /* Add % register prefix. */ - vec_add1 (s, '%'); - - switch (log2_n_bytes) - { - case 0: - { - - if (r < 8) - s = format (s, "%c%c", names8[r & 3], (r >> 2) ? 'l' : 'h'); - else - s = format (s, "r%db", r); - } - break; - - case 2: - case 3: - s = format (s, "%c", log2_n_bytes == 2 ? 'e' : 'r'); - /* fall through */ - case 1: - if (r < 8) - s = format (s, "%c%c", names8[r], names16[r]); - else - { - s = format (s, "%d", r); - if (log2_n_bytes != 3) - s = format (s, "%c", log2_n_bytes == 1 ? 'w' : 'd'); - } - break; - - default: - ASSERT (0); - } - - return s; -} - -static u8 * format_x86_reg_operand (u8 * s, va_list * va) -{ - u32 reg = va_arg (*va, u32); - u32 log2_n_bytes = va_arg (*va, u32); - u32 type = va_arg (*va, u32); - - switch (type) - { - default: - ASSERT (0); - break; - - case 'x': - ASSERT (reg < 16); - return format (s, "%%xmm%d", reg); - - case 'm': - ASSERT (reg < 8); - return format (s, "%%mm%d", reg); - - /* Explicit byte/word/double-word/quad-word */ - case 'b': log2_n_bytes = 0; break; - case 'w': log2_n_bytes = 1; break; - case 'd': log2_n_bytes = 2; break; - case 'q': log2_n_bytes = 3; break; - - /* Use effective operand size. */ - case 'v': break; - - /* word or double-word depending on effective operand size. */ - case 'z': - log2_n_bytes = clib_min (log2_n_bytes, 2); - break; - } - - s = format (s, "%U", format_x86_gp_reg_operand, reg, log2_n_bytes); - return s; -} - -static u8 * format_x86_mem_operand (u8 * s, va_list * va) -{ - x86_insn_parse_t * p = va_arg (*va, x86_insn_parse_t *); - - if (p->displacement != 0) - s = format (s, "0x%x", p->displacement); - - if (p->flags & X86_INSN_HAS_BASE) - { - s = format (s, "(%U", - format_x86_gp_reg_operand, p->regs[1], - p->log2_effective_address_bytes); - if (p->flags & X86_INSN_HAS_INDEX) - { - s = format (s, ",%U", - format_x86_gp_reg_operand, p->regs[2], - p->log2_effective_address_bytes); - if (p->log2_index_scale != 0) - s = format (s, ",%d", 1 << p->log2_index_scale); - } - s = format (s, ")"); - } - - /* [RIP+disp] PC relative addressing in 64 bit mode. */ - else if (p->flags & X86_INSN_PARSE_64_BIT) - s = format (s, "(%%rip)"); - - return s; -} - -static u8 * format_x86_insn_operand (u8 * s, va_list * va) -{ - x86_insn_parse_t * p = va_arg (*va, x86_insn_parse_t *); - x86_insn_t * insn = &p->insn; - u32 o = va_arg (*va, u32); - u8 c, t; - - ASSERT (o < ARRAY_LEN (insn->operands)); - c = insn->operands[o].code; - t = insn->operands[o].type; - - /* Register encoded in instruction. */ - if (c < 8) - return format (s, "%U", - format_x86_gp_reg_operand, c, - p->log2_effective_operand_bytes); - - switch (c) - { - /* Memory or reg field from modrm byte. */ - case 'M': - ASSERT (p->flags & X86_INSN_IS_ADDRESS); - /* FALLTHROUGH */ - case 'E': - if (p->flags & X86_INSN_IS_ADDRESS) - s = format (s, "%U", format_x86_mem_operand, p); - else - s = format (s, "%U", - format_x86_reg_operand, p->regs[1], - p->log2_effective_operand_bytes, t); - break; - - /* reg field from modrm byte. */ - case 'R': - case 'G': - s = format (s, "%U", - format_x86_reg_operand, p->regs[0], - p->log2_effective_operand_bytes, t); - break; - - case 'I': - { - u32 l = x86_insn_log2_immediate_bytes (p, insn); - i64 mask = pow2_mask (8ULL << l); - s = format (s, "$0x%Lx", p->immediate & mask); - } - break; - - case 'J': - if (p->immediate < 0) - s = format (s, "- 0x%Lx", -p->immediate); - else - s = format (s, "+ 0x%Lx", p->immediate); - break; - - case 'O': - s = format (s, "0x%Lx", p->immediate); - break; - - case 'A': - /* AX/AL */ - s = format (s, "%U", - format_x86_gp_reg_operand, X86_INSN_GP_REG_AX, - t == 'L' ? 0 : p->log2_effective_operand_bytes); - break; - - case 'B': - /* BX/BL/BP */ - s = format (s, "%U", - format_x86_gp_reg_operand, - t == 'P' ? X86_INSN_GP_REG_BP : X86_INSN_GP_REG_BX, - t == 'L' ? 0 : p->log2_effective_operand_bytes); - break; - - case 'C': - /* CX/CL */ - s = format (s, "%U", - format_x86_gp_reg_operand, X86_INSN_GP_REG_CX, - t == 'L' ? 0 : p->log2_effective_operand_bytes); - break; - - case 'D': - /* DX/DL/DI */ - s = format (s, "%U", - format_x86_gp_reg_operand, - t == 'I' ? X86_INSN_GP_REG_DI : X86_INSN_GP_REG_DX, - t == 'L' ? 0 : p->log2_effective_operand_bytes); - break; - - case 'S': - /* SI/SP */ - s = format (s, "%U", - format_x86_gp_reg_operand, - t == 'I' ? X86_INSN_GP_REG_SI : X86_INSN_GP_REG_SP, - p->log2_effective_operand_bytes); - break; - - case '1': - s = format (s, "1"); - break; - - default: - ASSERT (0); - } - - return s; -} - -u8 * format_x86_insn_parse (u8 * s, va_list * va) -{ - x86_insn_parse_t * p = va_arg (*va, x86_insn_parse_t *); - x86_insn_t * insn = &p->insn; - u32 o, i, is_src_dst; - - s = format (s, "%s", insn->name); - - if (! x86_insn_operand_is_valid (insn, 0)) - goto done; - - is_src_dst = x86_insn_operand_is_valid (insn, 1); - - /* If instruction has immediate add suffix to opcode to - indicate operand size. */ - if (is_src_dst) - { - u32 b; - - b = x86_insn_log2_immediate_bytes (p, insn); - if (b < p->log2_effective_operand_bytes - && (p->flags & X86_INSN_IS_ADDRESS)) - s = format (s, "%c", "bwlq"[b]); - } - - for (i = 0; i < ARRAY_LEN (insn->operands); i++) - { - o = is_src_dst + i; - if (! x86_insn_operand_is_valid (insn, o)) - break; - s = format (s, "%s%U", - i == 0 ? " " : ", ", - format_x86_insn_operand, p, o); - } - - if (is_src_dst) - s = format (s, ", %U", - format_x86_insn_operand, p, 0); - - done: - return s; -} diff --git a/src/vppinfra/asm_x86.h b/src/vppinfra/asm_x86.h deleted file mode 100644 index dacef61755c..00000000000 --- a/src/vppinfra/asm_x86.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2015 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef included_asm_x86_h -#define included_asm_x86_h - -#include <vppinfra/format.h> - -typedef union -{ - struct - { - u8 code; - u8 type; - }; - u8 data[2]; -} x86_insn_operand_t; - -typedef struct -{ - /* Instruction name. */ - char *name; - - /* X86 instructions may have up to 3 operands. */ - x86_insn_operand_t operands[3]; - - u16 flags; -#define X86_INSN_FLAG_DEFAULT_64_BIT (1 << 0) -#define X86_INSN_FLAG_SET_SSE_GROUP(n) ((n) << 5) -#define X86_INSN_FLAG_GET_SSE_GROUP(f) (((f) >> 5) & 0x1f) -#define X86_INSN_FLAG_SET_MODRM_REG_GROUP(n) (((n) & 0x3f) << 10) -#define X86_INSN_FLAG_GET_MODRM_REG_GROUP(f) (((f) >> 10) & 0x3f) -} x86_insn_t; - -always_inline uword -x86_insn_operand_is_valid (x86_insn_t * i, uword o) -{ - ASSERT (o < ARRAY_LEN (i->operands)); - return i->operands[o].code != '_'; -} - -#define foreach_x86_legacy_prefix \ - _ (OPERAND_SIZE, 0x66) \ - _ (ADDRESS_SIZE, 0x67) \ - _ (SEGMENT_CS, 0x2e) \ - _ (SEGMENT_DS, 0x3e) \ - _ (SEGMENT_ES, 0x26) \ - _ (SEGMENT_FS, 0x64) \ - _ (SEGMENT_GS, 0x65) \ - _ (SEGMENT_SS, 0x36) \ - _ (LOCK, 0xf0) \ - _ (REPZ, 0xf3) \ - _ (REPNZ, 0xf2) - -#define foreach_x86_insn_parse_flag \ - /* Parse in 32/64-bit mode. */ \ - _ (PARSE_32_BIT, 0) \ - _ (PARSE_64_BIT, 0) \ - _ (IS_ADDRESS, 0) \ - /* regs[1/2] is a valid base/index register */ \ - _ (HAS_BASE, 0) \ - _ (HAS_INDEX, 0) \ - /* rex w bit */ \ - _ (OPERAND_SIZE_64, 0) - -typedef enum -{ -#define _(f,o) X86_INSN_FLAG_BIT_##f, - foreach_x86_insn_parse_flag foreach_x86_legacy_prefix -#undef _ -} x86_insn_parse_flag_bit_t; - -typedef enum -{ -#define _(f,o) X86_INSN_##f = 1 << X86_INSN_FLAG_BIT_##f, - foreach_x86_insn_parse_flag foreach_x86_legacy_prefix -#undef _ -} x86_insn_parse_flag_t; - -typedef struct -{ - /* Registers in instruction. - [0] is modrm reg field - [1] is base reg - [2] is index reg. */ - u8 regs[3]; - - /* Scale for index register. */ - u8 log2_index_scale:2; - u8 log2_effective_operand_bytes:3; - u8 log2_effective_address_bytes:3; - - i32 displacement; - - /* Parser flags: set of x86_insn_parse_flag_t enums. */ - u32 flags; - - i64 immediate; - - x86_insn_t insn; -} x86_insn_parse_t; - -u8 *x86_insn_parse (x86_insn_parse_t * p, u8 * code_start); -format_function_t format_x86_insn_parse; - -#endif /* included_asm_x86_h */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/vppinfra/backtrace.c b/src/vppinfra/backtrace.c deleted file mode 100644 index e713bae6876..00000000000 --- a/src/vppinfra/backtrace.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2015 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - Copyright (c) 2004 Eliot Dresselhaus - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include <vppinfra/clib.h> -#include <vppinfra/error.h> - -#ifdef __mips__ - -/* Let code below know we've defined _clib_backtrace */ -#define clib_backtrace_defined - -#include <vppinfra/asm_mips.h> - -__clib_export uword -clib_backtrace (uword * callers, uword max_callers, uword n_frames_to_skip) -{ - u32 *pc; - void *sp; - uword i, saved_pc; - - /* Figure current PC, saved PC and stack pointer. */ - asm volatile (".set push\n" - ".set noat\n" "move %[saved_pc], $31\n" "move %[sp], $29\n" - /* Fetches current PC. */ - "la $at, 1f\n" - "jalr %[pc], $at\n" - "nop\n" - "1:\n" - ".set pop\n":[pc] "=r" (pc), - [saved_pc] "=r" (saved_pc),[sp] "=r" (sp)); - - /* Also skip current frame. */ - n_frames_to_skip += 1; - - for (i = 0; i < max_callers + n_frames_to_skip; i++) - { - mips_insn_opcode_t op; - mips_insn_special_funct_t funct; - i32 insn, rs, rt, rd, immediate, found_saved_pc; - u32 *start_pc; - - /* Parse instructions until we reach prologue for this - stack frame. We'll need to figure out where saved - PC is and where previous stack frame lives. */ - start_pc = pc; - found_saved_pc = 0; - while (1) - { - insn = *--pc; - op = mips_insn_get_op (insn); - funct = mips_insn_get_funct (insn); - rs = mips_insn_get_rs (insn); - rt = mips_insn_get_rt (insn); - rd = mips_insn_get_rd (insn); - immediate = mips_insn_get_immediate (insn); - - switch (op) - { - default: - break; - - case MIPS_OPCODE_sd: - case MIPS_OPCODE_sw: - /* Trace stores of return address. */ - if (rt == MIPS_REG_RA) - { - void *addr = sp + immediate; - - /* If RA is stored somewhere other than in the - stack frame, give up. */ - if (rs != MIPS_REG_SP) - goto backtrace_done; - - ASSERT (immediate % 4 == 0); - if (op == MIPS_OPCODE_sw) - saved_pc = ((u32 *) addr)[0]; - else - saved_pc = ((u64 *) addr)[0]; - found_saved_pc = 1; - } - break; - - case MIPS_OPCODE_addiu: - case MIPS_OPCODE_daddiu: - case MIPS_OPCODE_addi: - case MIPS_OPCODE_daddi: - if (rt == MIPS_REG_SP) - { - if (rs != MIPS_REG_SP) - goto backtrace_done; - - ASSERT (immediate % 4 == 0); - - /* Assume positive offset is part of the epilogue. - E.g. - jr ra - add sp,sp,100 - */ - if (immediate > 0) - continue; - - /* Negative offset means allocate stack space. - This could either be the prologue or could be due to - alloca. */ - sp -= immediate; - - /* This frame will not save RA. */ - if (i == 0) - goto found_prologue; - - /* Assume that addiu sp,sp,-N without store of ra means - that we have not found the prologue yet. */ - if (found_saved_pc) - goto found_prologue; - } - break; - - case MIPS_OPCODE_slti: - case MIPS_OPCODE_sltiu: - case MIPS_OPCODE_andi: - case MIPS_OPCODE_ori: - case MIPS_OPCODE_xori: - case MIPS_OPCODE_lui: - case MIPS_OPCODE_ldl: - case MIPS_OPCODE_ldr: - case MIPS_OPCODE_lb: - case MIPS_OPCODE_lh: - case MIPS_OPCODE_lwl: - case MIPS_OPCODE_lw: - case MIPS_OPCODE_lbu: - case MIPS_OPCODE_lhu: - case MIPS_OPCODE_lwr: - case MIPS_OPCODE_lwu: - case MIPS_OPCODE_ld: - /* Give up when we find anyone setting the stack pointer. */ - if (rt == MIPS_REG_SP) - goto backtrace_done; - break; - - case MIPS_OPCODE_SPECIAL: - if (rd == MIPS_REG_SP) - switch (funct) - { - default: - /* Give up when we find anyone setting the stack pointer. */ - goto backtrace_done; - - case MIPS_SPECIAL_FUNCT_break: - case MIPS_SPECIAL_FUNCT_jr: - case MIPS_SPECIAL_FUNCT_sync: - case MIPS_SPECIAL_FUNCT_syscall: - case MIPS_SPECIAL_FUNCT_tge: - case MIPS_SPECIAL_FUNCT_tgeu: - case MIPS_SPECIAL_FUNCT_tlt: - case MIPS_SPECIAL_FUNCT_tltu: - case MIPS_SPECIAL_FUNCT_teq: - case MIPS_SPECIAL_FUNCT_tne: - /* These instructions can validly have rd == MIPS_REG_SP */ - break; - } - break; - } - } - - found_prologue: - /* Check sanity of saved pc. */ - if (saved_pc & 3) - goto backtrace_done; - if (saved_pc == 0) - goto backtrace_done; - - if (i >= n_frames_to_skip) - callers[i - n_frames_to_skip] = saved_pc; - pc = uword_to_pointer (saved_pc, u32 *); - } - -backtrace_done: - if (i < n_frames_to_skip) - return 0; - else - return i - n_frames_to_skip; -} -#endif /* __mips__ */ - -#ifndef clib_backtrace_defined -#define clib_backtrace_defined - -/* use glibc backtrace for stack trace */ -#include <execinfo.h> - -__clib_export uword -clib_backtrace (uword * callers, uword max_callers, uword n_frames_to_skip) -{ - int size; - void *array[20]; - /* Also skip current frame. */ - n_frames_to_skip += 1; - - size = clib_min (ARRAY_LEN (array), max_callers + n_frames_to_skip); - - size = backtrace (array, size); - - uword i; - - for (i = 0; i < max_callers + n_frames_to_skip && i < size; i++) - { - if (i >= n_frames_to_skip) - callers[i - n_frames_to_skip] = pointer_to_uword (array[i]); - } - - if (i < n_frames_to_skip) - return 0; - else - return i - n_frames_to_skip; -} - - -#endif /* clib_backtrace_defined */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/vppinfra/cJSON.c b/src/vppinfra/cJSON.c index 448435de4dc..24e0110ed08 100644 --- a/src/vppinfra/cJSON.c +++ b/src/vppinfra/cJSON.c @@ -20,6 +20,7 @@ THE SOFTWARE. */ /* clang-format off */ + /* cJSON */ /* JSON parser in C. */ @@ -96,9 +97,9 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) return (const char*) (global_error.json + global_error.position); } -CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +CJSON_PUBLIC (char *) cJSON_GetStringValue (const cJSON *const item) { - if (!cJSON_IsString(item)) + if (!cJSON_IsString (item)) { return NULL; } @@ -106,9 +107,9 @@ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) return item->valuestring; } -CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +CJSON_PUBLIC (double) cJSON_GetNumberValue (const cJSON *const item) { - if (!cJSON_IsNumber(item)) + if (!cJSON_IsNumber (item)) { return (double) NAN; } @@ -117,8 +118,9 @@ CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) } /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ -#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 14) - #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || \ + (CJSON_VERSION_PATCH != 17) +#error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif CJSON_PUBLIC(const char*) cJSON_Version(void) @@ -157,7 +159,7 @@ typedef struct internal_hooks { void *(CJSON_CDECL *allocate)(size_t size); void (CJSON_CDECL *deallocate)(void *pointer); - void *(CJSON_CDECL *reallocate)(void *pointer, size_t new_size, size_t old_size); + void *(CJSON_CDECL *reallocate) (void *pointer, size_t size); } internal_hooks; #if defined(_MSC_VER) @@ -170,20 +172,17 @@ static void CJSON_CDECL internal_free(void *pointer) { free(pointer); } +static void *CJSON_CDECL +internal_realloc (void *pointer, size_t size) +{ + return realloc (pointer, size); +} #else #define internal_malloc malloc #define internal_free free +#define internal_realloc realloc #endif -static void * CJSON_CDECL internal_realloc(void *pointer, size_t new_size, - size_t old_size) -{ - return realloc(pointer, new_size); -} - -static void * -cjson_realloc_internal (void *ptr, size_t new_size, size_t old_size); - /* strlen of character literals resolved at compile time */ #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) @@ -217,8 +216,8 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) /* Reset hooks */ global_hooks.allocate = malloc; global_hooks.deallocate = free; - global_hooks.reallocate = internal_realloc; - return; + global_hooks.reallocate = realloc; + return; } global_hooks.allocate = malloc; @@ -233,16 +232,11 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) global_hooks.deallocate = hooks->free_fn; } - /* use realloc only if both free and malloc are used */ - global_hooks.reallocate = NULL; - if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) - { - global_hooks.reallocate = internal_realloc; - } - else - { - global_hooks.reallocate = cjson_realloc_internal; - } + global_hooks.reallocate = realloc; + if (hooks->realloc_fn != NULL) + { + global_hooks.reallocate = hooks->realloc_fn; + } } /* Internal constructor. */ @@ -405,14 +399,22 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) return object->valuedouble = number; } +/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as + * an error and return NULL */ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) { char *copy = NULL; /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ - if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) - { - return NULL; - } + if ((object == NULL) || !(object->type & cJSON_String) || + (object->type & cJSON_IsReference)) + { + return NULL; + } + /* return NULL if the object is corrupted or valuestring is NULL */ + if (object->valuestring == NULL || valuestring == NULL) + { + return NULL; + } if (strlen(valuestring) <= strlen(object->valuestring)) { strcpy(object->valuestring, valuestring); @@ -443,27 +445,6 @@ typedef struct internal_hooks hooks; } printbuffer; -static void * -cjson_realloc_internal (void *ptr, size_t new_size, size_t old_size) -{ - size_t copy_size; - if (old_size < new_size) - copy_size = old_size; - else - copy_size = new_size; - - unsigned char *newbuffer = global_hooks.allocate(new_size); - if (!newbuffer) - { - global_hooks.deallocate(ptr); - return NULL; - } - - memcpy (newbuffer, ptr, copy_size); - global_hooks.deallocate (ptr); - return newbuffer; -} - /* realloc printbuffer if necessary to have at least "needed" bytes more */ static unsigned char* ensure(printbuffer * const p, size_t needed) { @@ -515,14 +496,35 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) newsize = needed * 2; } - newbuffer = p->hooks.reallocate (p->buffer, newsize, p->length); - if (newbuffer == NULL) - { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - return NULL; - } + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char *) p->hooks.reallocate (p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate (p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char *) p->hooks.allocate (newsize); + if (!newbuffer) + { + p->hooks.deallocate (p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + + memcpy (newbuffer, p->buffer, p->offset + 1); + p->hooks.deallocate (p->buffer); + } p->length = newsize; p->buffer = newbuffer; @@ -570,6 +572,10 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out { length = sprintf((char*)number_buffer, "null"); } + else if (d == (double) item->valueint) + { + length = sprintf ((char *) number_buffer, "%d", item->valueint); + } else { /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ @@ -1111,7 +1117,7 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer } buffer.content = (const unsigned char*)value; - buffer.length = buffer_length; + buffer.length = buffer_length; buffer.offset = 0; buffer.hooks = global_hooks; @@ -1216,11 +1222,13 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i /* check if reallocate is available */ if (hooks->reallocate != NULL) { - printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1, default_buffer_size); - if (printed == NULL) { - goto fail; - } - buffer->buffer = NULL; + printed = (unsigned char *) hooks->reallocate (buffer->buffer, + buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + buffer->buffer = NULL; } else /* otherwise copy the JSON over to a new buffer */ { @@ -1658,8 +1666,13 @@ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_bu current_item = new_item; } - /* parse the name of the child */ - input_buffer->offset++; + if (cannot_access_at_index (input_buffer, 1)) + { + goto fail; /* nothing comes after the comma */ + } + + /* parse the name of the child */ + input_buffer->offset++; buffer_skip_whitespace(input_buffer); if (!parse_string(current_item, input_buffer)) { @@ -2268,10 +2281,10 @@ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON { cJSON *after_inserted = NULL; - if (which < 0) - { - return false; - } + if (which < 0 || newitem == NULL) + { + return false; + } after_inserted = get_array_item(array, (size_t)which); if (after_inserted == NULL) @@ -2279,6 +2292,12 @@ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON return add_item_to_array(array, newitem); } + if (after_inserted != array->child && after_inserted->prev == NULL) + { + /* return false if after_inserted is a corrupted array item */ + return false; + } + newitem->next = after_inserted; newitem->prev = after_inserted->prev; after_inserted->prev = newitem; @@ -2295,7 +2314,8 @@ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) { - if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || + (item == NULL)) { return false; } @@ -2365,6 +2385,11 @@ static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSO cJSON_free(replacement->string); } replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if (replacement->string == NULL) + { + return false; + } + replacement->type &= ~cJSON_StringIsConst; return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); @@ -2639,9 +2664,9 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) for (i = 0; a && (i < (size_t) count); i++) { - n = cJSON_CreateNumber(numbers[i]); - if(!n) - { + n = cJSON_CreateNumber (numbers[i]); + if (!n) + { cJSON_Delete(a); return NULL; } @@ -2988,7 +3013,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) { - if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { return false; } @@ -3121,7 +3146,7 @@ CJSON_PUBLIC(void) cJSON_free(void *object) global_hooks.deallocate(object); } -CJSON_PUBLIC(void *) cJSON_realloc(void *object, size_t new_size, size_t old_size) +CJSON_PUBLIC (void *) cJSON_realloc (void *object, size_t size) { - return global_hooks.reallocate(object, new_size, old_size); + return global_hooks.reallocate (object, size); } diff --git a/src/vppinfra/cJSON.h b/src/vppinfra/cJSON.h index 1474c4e5c49..1c98dfac70e 100644 --- a/src/vppinfra/cJSON.h +++ b/src/vppinfra/cJSON.h @@ -81,7 +81,7 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ /* project version */ #define CJSON_VERSION_MAJOR 1 #define CJSON_VERSION_MINOR 7 -#define CJSON_VERSION_PATCH 14 +#define CJSON_VERSION_PATCH 17 #include <stddef.h> @@ -127,8 +127,7 @@ typedef struct cJSON_Hooks /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ void *(CJSON_CDECL *malloc_fn)(size_t sz); void (CJSON_CDECL *free_fn)(void *ptr); - void *(CJSON_CDECL *realloc_fn) (void *ptr, size_t new_size, - size_t old_size); + void *(CJSON_CDECL *realloc_fn) (void *ptr, size_t sz); } cJSON_Hooks; typedef int cJSON_bool; @@ -256,9 +255,10 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); -/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. - * The input pointer json cannot point to a read-only address area, such as a string constant, - * but should point to a readable and writable adress area. */ +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') + * from strings. The input pointer json cannot point to a read-only address + * area, such as a string constant, + * but should point to a readable and writable address area. */ CJSON_PUBLIC(void) cJSON_Minify(char *json); /* Helper functions for creating and adding items to an object at the same time. @@ -281,14 +281,21 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); +/* If the object is not a boolean type this does nothing and returns + * cJSON_Invalid else it returns the new type*/ +#define cJSON_SetBoolValue(object, boolValue) \ + ((object != NULL && ((object)->type & (cJSON_False | cJSON_True))) ? \ + (object)->type = ((object)->type & (~(cJSON_False | cJSON_True))) | \ + ((boolValue) ? cJSON_True : cJSON_False) : \ + cJSON_Invalid) + /* Macro for iterating over an array or object */ #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ CJSON_PUBLIC(void *) cJSON_malloc(size_t size); CJSON_PUBLIC(void) cJSON_free(void *object); -CJSON_PUBLIC (void *) -cJSON_realloc (void *object, size_t new_size, size_t old_size); +CJSON_PUBLIC (void *) cJSON_realloc (void *object, size_t size); #ifdef __cplusplus } diff --git a/src/vppinfra/clib.h b/src/vppinfra/clib.h index d14582492d6..75cebc65672 100644 --- a/src/vppinfra/clib.h +++ b/src/vppinfra/clib.h @@ -385,10 +385,6 @@ void qsort (void *base, uword n, uword size, int (*)(const void *, const void *)); #endif -/* Stack backtrace. */ -uword -clib_backtrace (uword * callers, uword max_callers, uword n_frames_to_skip); - #include <vppinfra/byte_order.h> #endif /* included_clib_h */ diff --git a/src/vppinfra/format_ansi.h b/src/vppinfra/format_ansi.h new file mode 100644 index 00000000000..c35406aacf7 --- /dev/null +++ b/src/vppinfra/format_ansi.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 Cisco Systems, Inc. + */ + +#ifndef __FORMAT_ANSI_H__ +#define __FORMAT_ANSI_H__ + +#define ANSI_RESET "\x1b[0m" +#define ANSI_BOLD "\x1b[1m" +#define ANSI_ITALIC "\x1b[3m" +#define ANSI_UNDERLINE "\x1b[4m" +#define ANSI_BLINK "\x1b[5m" +#define ANSI_FG_BLACK "\x1b[30m" +#define ANSI_FG_RED "\x1b[31m" +#define ANSI_FG_GREEN "\x1b[32m" +#define ANSI_FG_YELLOW "\x1b[33m" +#define ANSI_FG_BLUE "\x1b[34m" +#define ANSI_FG_MAGENTA "\x1b[35m" +#define ANSI_FG_CYAN "\x1b[36m" +#define ANSI_FG_WHITE "\x1b[37m" +#define ANSI_FG_DEFAULT "\x1b[39m" +#define ANSI_BG_BLACK "\x1b[40m" +#define ANSI_BG_RED "\x1b[41m" +#define ANSI_BG_GREEN "\x1b[42m" +#define ANSI_BG_YELLOW "\x1b[43m" +#define ANSI_BG_BLUE "\x1b[44m" +#define ANSI_BG_MAGENTA "\x1b[45m" +#define ANSI_BG_CYAN "\x1b[46m" +#define ANSI_BG_WHITE "\x1b[47m" +#define ANSI_BG_DEFAULT "\x1b[49m" +#define ANSI_FG_BR_BLACK "\x1b[90m" +#define ANSI_FG_BR_RED "\x1b[91m" +#define ANSI_FG_BR_GREEN "\x1b[92m" +#define ANSI_FG_BR_YELLOW "\x1b[93m" +#define ANSI_FG_BR_BLUE "\x1b[94m" +#define ANSI_FG_BR_MAGENTA "\x1b[95m" +#define ANSI_FG_BR_CYAN "\x1b[96m" +#define ANSI_FG_BR_WHITE "\x1b[97m" +#define ANSI_BG_BR_BLACK "\x1b[100m" +#define ANSI_BG_BR_RED "\x1b[101m" +#define ANSI_BG_BR_GREEN "\x1b[102m" +#define ANSI_BG_BR_YELLOW "\x1b[103m" +#define ANSI_BG_BR_BLUE "\x1b[104m" +#define ANSI_BG_BR_MAGENTA "\x1b[105m" +#define ANSI_BG_BR_CYAN "\x1b[106m" +#define ANSI_BG_BR_WHITE "\x1b[107m" + +#endif /* __FORMAT_ANSI_H__ */ diff --git a/src/vppinfra/heap.c b/src/vppinfra/heap.c index 7db814200f8..9920528732d 100644 --- a/src/vppinfra/heap.c +++ b/src/vppinfra/heap.c @@ -680,6 +680,7 @@ debug_elt (u8 * s, void *v, word i, word n) i = -n / 2; for (e = e0; 1; e = heap_next (e)) { + s = format (s, " "); if (heap_is_free (e)) s = format (s, "index %4d, free\n", e - h->elts); else if (h->format_elt) diff --git a/src/vppinfra/mem_dlmalloc.c b/src/vppinfra/mem_dlmalloc.c index a188164a7ba..e98687fff2a 100644 --- a/src/vppinfra/mem_dlmalloc.c +++ b/src/vppinfra/mem_dlmalloc.c @@ -19,6 +19,7 @@ #include <vppinfra/lock.h> #include <vppinfra/hash.h> #include <vppinfra/elf_clib.h> +#include <vppinfra/stack.h> typedef struct { @@ -65,15 +66,13 @@ mheap_get_trace_internal (const clib_mem_heap_t *heap, uword offset, { mheap_trace_main_t *tm = &mheap_trace_main; mheap_trace_t *t; - uword i, n_callers, trace_index, *p; - mheap_trace_t trace; + uword i, trace_index, *p; + mheap_trace_t trace = {}; + int index; if (heap != tm->current_traced_mheap || mheap_trace_thread_disable) return; - /* Spurious Coverity warnings be gone. */ - clib_memset (&trace, 0, sizeof (trace)); - clib_spinlock_lock (&tm->lock); /* heap could have changed while we were waiting on the lock */ @@ -83,9 +82,19 @@ mheap_get_trace_internal (const clib_mem_heap_t *heap, uword offset, /* Turn off tracing for this thread to avoid embarrassment... */ mheap_trace_thread_disable = 1; - /* Skip our frame and mspace_get_aligned's frame */ - n_callers = clib_backtrace (trace.callers, ARRAY_LEN (trace.callers), 2); - if (n_callers == 0) + index = -2; /* skip first 2 stack frames */ + foreach_clib_stack_frame (sf) + { + if (index >= 0) + { + if (index == ARRAY_LEN (trace.callers)) + break; + trace.callers[index] = sf->ip; + } + index++; + } + + if (index < 1) goto out; if (!tm->trace_by_callers) diff --git a/src/vppinfra/mhash.c b/src/vppinfra/mhash.c index f0f1aa470d7..babaaeec726 100644 --- a/src/vppinfra/mhash.c +++ b/src/vppinfra/mhash.c @@ -164,6 +164,8 @@ mhash_sanitize_hash_user (mhash_t * mh) h->user = pointer_to_uword (mh); } +static u8 *mhash_format_pair_default (u8 *s, va_list *args); + __clib_export void mhash_init (mhash_t * h, uword n_value_bytes, uword n_key_bytes) { @@ -208,12 +210,12 @@ mhash_init (mhash_t * h, uword n_value_bytes, uword n_key_bytes) vec_validate (h->key_tmps, os_get_nthreads () - 1); ASSERT (n_key_bytes < ARRAY_LEN (t)); - h->hash = hash_create2 ( /* elts */ 0, + h->hash = hash_create2 (/* elts */ 0, /* user */ pointer_to_uword (h), /* value_bytes */ n_value_bytes, t[n_key_bytes].key_sum, t[n_key_bytes].key_equal, /* format pair/arg */ - 0, 0); + mhash_format_pair_default, 0); } static uword @@ -331,8 +333,8 @@ mhash_set_mem (mhash_t * h, void *key, uword * new_value, uword * old_value) { if (key_alloc_from_free_list) { - h->key_vector_free_indices[l] = i; - vec_set_len (h->key_vector_free_indices, l + 1); + vec_set_len (h->key_vector_free_indices, l); + h->key_vector_free_indices[l - 1] = i; } else vec_dec_len (h->key_vector_or_heap, h->n_key_bytes); @@ -371,8 +373,8 @@ mhash_unset (mhash_t * h, void *key, uword * old_value) return 1; } -u8 * -format_mhash_key (u8 * s, va_list * va) +__clib_export u8 * +format_mhash_key (u8 *s, va_list *va) { mhash_t *h = va_arg (*va, mhash_t *); u32 ki = va_arg (*va, u32); @@ -387,7 +389,43 @@ format_mhash_key (u8 * s, va_list * va) else if (h->format_key) s = format (s, "%U", h->format_key, k); else - s = format (s, "%U", format_hex_bytes, k, h->n_key_bytes); + s = format (s, "0x%U", format_hex_bytes, k, h->n_key_bytes); + + return s; +} + +static u8 * +mhash_format_pair_default (u8 *s, va_list *args) +{ + void *CLIB_UNUSED (user_arg) = va_arg (*args, void *); + void *v = va_arg (*args, void *); + hash_pair_t *p = va_arg (*args, hash_pair_t *); + hash_t *h = hash_header (v); + mhash_t *mh = uword_to_pointer (h->user, mhash_t *); + + s = format (s, "%U", format_mhash_key, mh, (u32) p->key); + if (hash_value_bytes (h) > 0) + s = format (s, " -> 0x%8U", format_hex_bytes, &p->value[0], + hash_value_bytes (h)); + return s; +} + +__clib_export u8 * +format_mhash (u8 *s, va_list *va) +{ + mhash_t *h = va_arg (*va, mhash_t *); + int verbose = va_arg (*va, int); + + s = format (s, "mhash %p, %wd elts, \n", h, mhash_elts (h)); + if (mhash_key_vector_is_heap (h)) + s = format (s, " %U", format_heap, h->key_vector_or_heap, verbose); + else + s = format (s, " keys %wd elts, %wd size, %wd free, %wd bytes used\n", + vec_len (h->key_vector_or_heap) / h->n_key_bytes, + h->n_key_bytes, vec_len (h->key_vector_free_indices), + vec_bytes (h->key_vector_or_heap) + + vec_bytes (h->key_vector_free_indices)); + s = format (s, " %U", format_hash, h->hash, verbose); return s; } diff --git a/src/vppinfra/mhash.h b/src/vppinfra/mhash.h index 7eb1918384e..62aee365fa3 100644 --- a/src/vppinfra/mhash.h +++ b/src/vppinfra/mhash.h @@ -166,8 +166,13 @@ do { \ })); \ } while (0) +u8 *format_mhash (u8 *s, va_list *va); + format_function_t format_mhash_key; +/* Main test routine. */ +int test_mhash_main (unformat_input_t *input); + #endif /* included_clib_mhash_h */ /* diff --git a/src/vppinfra/stack.c b/src/vppinfra/stack.c new file mode 100644 index 00000000000..190e880c228 --- /dev/null +++ b/src/vppinfra/stack.c @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 Cisco Systems, Inc. + */ + +#define _GNU_SOURCE +#include <dlfcn.h> + +#include <vppinfra/clib.h> +#include <vppinfra/stack.h> +#include <vppinfra/error.h> + +#if HAVE_LIBUNWIND == 1 + +#define UNW_LOCAL_ONLY +#include <libunwind.h> + +static __thread unw_cursor_t cursor; +static __thread unw_context_t context; + +#endif + +__clib_export clib_stack_frame_t * +clib_stack_frame_get (clib_stack_frame_t *sf) +{ +#if HAVE_LIBUNWIND == 1 + Dl_info info = {}; + + if (sf->index == 0) + { + if (unw_getcontext (&context) < 0) + { + clib_warning ("libunwind: cannot get local machine state\n"); + return 0; + } + if (unw_init_local (&cursor, &context) < 0) + { + clib_warning ( + "libunwind: cannot initialize cursor for local unwinding\n"); + return 0; + } + if (unw_step (&cursor) < 1) + return 0; + } + else if (unw_step (&cursor) < 1) + return 0; + + if (unw_get_reg (&cursor, UNW_REG_IP, &sf->ip)) + { + clib_warning ("libunwind: cannot read IP\n"); + return 0; + } + + if (unw_get_reg (&cursor, UNW_REG_SP, &sf->sp)) + { + clib_warning ("libunwind: cannot read SP\n"); + return 0; + } + + if (unw_get_proc_name (&cursor, sf->name, sizeof (sf->name), &sf->offset) < + 0) + sf->name[0] = sf->offset = 0; + + sf->is_signal_frame = unw_is_signal_frame (&cursor) ? 1 : 0; + + if (dladdr ((void *) sf->ip, &info)) + sf->file_name = info.dli_fname; + else + sf->file_name = 0; + + sf->index++; + return sf; +#else + return 0; +#endif +} diff --git a/src/vppinfra/stack.h b/src/vppinfra/stack.h new file mode 100644 index 00000000000..98a621d4176 --- /dev/null +++ b/src/vppinfra/stack.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 Cisco Systems, Inc. + */ + +#ifndef __STACK_H__ +#define __STACK_H__ + +#include <vppinfra/clib.h> + +typedef struct +{ + uword ip, sp; + uword offset; + char name[64]; + const char *file_name; + u32 index; + u8 is_signal_frame; +} clib_stack_frame_t; + +clib_stack_frame_t *clib_stack_frame_get (clib_stack_frame_t *); + +#define foreach_clib_stack_frame(sf) \ + for (clib_stack_frame_t _sf = {}, *sf = clib_stack_frame_get (&_sf); sf; \ + sf = clib_stack_frame_get (sf)) + +#endif /* __STACK_H__ */ diff --git a/src/vppinfra/test_mhash.c b/src/vppinfra/test_mhash.c new file mode 100644 index 00000000000..70be2b9b382 --- /dev/null +++ b/src/vppinfra/test_mhash.c @@ -0,0 +1,403 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2023 Yandex LLC. + */ + +#ifdef CLIB_LINUX_KERNEL +#include <linux/unistd.h> +#endif + +#ifdef CLIB_UNIX +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <vppinfra/time.h> +#endif + +#include <vppinfra/random.h> +#include <vppinfra/mem.h> +#include <vppinfra/hash.h> +#include <vppinfra/mhash.h> +#include <vppinfra/error.h> +#include <vppinfra/format.h> +#include <vppinfra/bitmap.h> + +static int verbose; +#define if_verbose(format, args...) \ + if (verbose) \ + { \ + clib_warning (format, ##args); \ + } + +typedef struct +{ + int n_iterations; + + int n_iterations_per_print; + + /* Number of pairs to insert into mhash. */ + int n_pairs; + + /* True to validate correctness of mhash functions. */ + int n_iterations_per_validate; + + /* Verbosity level for mhash formats. */ + int verbose; + + /* Random number seed. */ + u32 seed; +} mhash_test_t; + +static clib_error_t * +mhash_next_test (mhash_t *h) +{ + hash_next_t hn = { 0 }; + hash_pair_t *p0, *p1; + clib_error_t *error = 0; + + hash_foreach_pair (p0, h->hash, { + p1 = hash_next (h->hash, &hn); + error = CLIB_ERROR_ASSERT (p0 == p1); + if (error) + break; + }); + + if (!error) + error = CLIB_ERROR_ASSERT (!hash_next (h->hash, &hn)); + + return error; +} + +static clib_error_t * +test_word_key (mhash_test_t *ht) +{ + mhash_t _h = { 0 }, *h = &_h; + word i, j; + + word *keys = 0, *vals = 0; + uword *is_inserted = 0; + + clib_error_t *error = 0; + + vec_resize (keys, ht->n_pairs); + vec_resize (vals, vec_len (keys)); + + mhash_init (h, sizeof (vals[0]), sizeof (keys[0])); + /* borrow 0 elt to make index keys non-zero */ + vec_validate (h->key_vector_or_heap, 0); + + { + uword *unique = 0; + u32 k; + + for (i = 0; i < vec_len (keys); i++) + { + do + { + k = random_u32 (&ht->seed) & 0xfffff; + } + while (clib_bitmap_get (unique, k)); + unique = clib_bitmap_ori (unique, k); + keys[i] = k; + vals[i] = i; + } + + clib_bitmap_free (unique); + } + + for (i = 0; i < ht->n_iterations; i++) + { + u32 vi = random_u32 (&ht->seed) % vec_len (keys); + + if (clib_bitmap_get (is_inserted, vi)) + { + mhash_unset (h, &keys[vi], 0); + mhash_unset (h, &keys[vi], 0); + } + else + { + mhash_set (h, &keys[vi], vals[vi], 0); + mhash_set (h, &keys[vi], vals[vi], 0); + } + + is_inserted = clib_bitmap_xori (is_inserted, vi); + + if (ht->n_iterations_per_print > 0 && + ((i + 1) % ht->n_iterations_per_print) == 0) + if_verbose ("iteration %d\n %U", i + 1, format_mhash, h, ht->verbose); + + if (ht->n_iterations_per_validate == 0 || + (i + 1) % ht->n_iterations_per_validate) + continue; + + { + uword ki, *k, *v; + + mhash_foreach (k, v, h, { + ki = v[0]; + ASSERT (keys[ki] == k[0]); + }); + } + + if ((error = hash_validate (h->hash))) + goto done; + + for (j = 0; j < vec_len (keys); j++) + { + uword *v; + v = mhash_get (h, &keys[j]); + if ((error = CLIB_ERROR_ASSERT (clib_bitmap_get (is_inserted, j) == + (v != 0)))) + goto done; + if (v) + { + if ((error = CLIB_ERROR_ASSERT (v[0] == vals[j]))) + goto done; + } + } + } + + if ((error = mhash_next_test (h))) + goto done; + + if_verbose ("%U", format_mhash, h, ht->verbose); + + for (i = 0; i < vec_len (keys); i++) + { + if (!clib_bitmap_get (is_inserted, i)) + continue; + + mhash_unset (h, &keys[i], 0); + mhash_unset (h, &keys[i], 0); + is_inserted = clib_bitmap_xori (is_inserted, i); + + if (ht->n_iterations_per_validate == 0 || + (i + 1) % ht->n_iterations_per_validate) + continue; + + if ((error = hash_validate (h->hash))) + goto done; + + for (j = 0; j < vec_len (keys); j++) + { + uword *v; + v = mhash_get (h, &keys[j]); + if ((error = CLIB_ERROR_ASSERT (clib_bitmap_get (is_inserted, j) == + (v != 0)))) + goto done; + if (v) + { + if ((error = CLIB_ERROR_ASSERT (v[0] == vals[j]))) + goto done; + } + } + } + +done: + mhash_free (h); + vec_free (keys); + vec_free (vals); + clib_bitmap_free (is_inserted); + + if (verbose) + fformat (stderr, "%U\n", format_clib_mem_usage, /* verbose */ 0); + + return error; +} + +static u8 * +test2_format (u8 *s, va_list *args) +{ + void *CLIB_UNUSED (user_arg) = va_arg (*args, void *); + void *v = va_arg (*args, void *); + hash_pair_t *p = va_arg (*args, hash_pair_t *); + hash_t *h = hash_header (v); + mhash_t *mh = uword_to_pointer (h->user, mhash_t *); + + return format (s, "0x%8U <- %U", format_hex_bytes, &p->value[0], + hash_value_bytes (h), format_mhash_key, mh, (u32) p->key); +} + +static clib_error_t * +test_string_key (mhash_test_t *ht, uword is_c_string) +{ + mhash_t _h = { 0 }, *h = &_h; + word i, j; + + u8 **keys = 0; + word *vals = 0; + uword *is_inserted = 0; + + clib_error_t *error = 0; + + vec_resize (keys, ht->n_pairs); + vec_resize (vals, vec_len (keys)); + + if (is_c_string) + mhash_init_c_string (h, sizeof (vals[0])); + else + mhash_init_vec_string (h, sizeof (vals[0])); + hash_set_pair_format (h->hash, test2_format, 0); + + for (i = 0; i < vec_len (keys); i++) + { + keys[i] = random_string (&ht->seed, 5 + (random_u32 (&ht->seed) & 0xf)); + keys[i] = format (keys[i], "%x", i); + if (is_c_string) + vec_terminate_c_string (keys[i]); + vals[i] = random_u32 (&ht->seed); + } + + for (i = 0; i < ht->n_iterations; i++) + { + u32 vi = random_u32 (&ht->seed) % vec_len (keys); + + if (clib_bitmap_get (is_inserted, vi)) + { + mhash_unset (h, keys[vi], 0); + mhash_unset (h, keys[vi], 0); + } + else + { + mhash_set (h, keys[vi], vals[vi], 0); + mhash_set (h, keys[vi], vals[vi], 0); + } + + is_inserted = clib_bitmap_xori (is_inserted, vi); + + if (ht->n_iterations_per_print > 0 && + ((i + 1) % ht->n_iterations_per_print) == 0) + if_verbose ("iteration %d\n %U", i + 1, format_mhash, h, ht->verbose); + + if (ht->n_iterations_per_validate == 0 || + (i + 1) % ht->n_iterations_per_validate) + continue; + + if ((error = hash_validate (h->hash))) + goto done; + + for (j = 0; j < vec_len (keys); j++) + { + uword *v; + v = mhash_get (h, keys[j]); + if ((error = CLIB_ERROR_ASSERT (clib_bitmap_get (is_inserted, j) == + (v != 0)))) + goto done; + if (v) + { + if ((error = CLIB_ERROR_ASSERT (v[0] == vals[j]))) + goto done; + } + } + } + + if ((error = mhash_next_test (h))) + goto done; + + if_verbose ("%U", format_mhash, h, ht->verbose); + + for (i = 0; i < vec_len (keys); i++) + { + if (!clib_bitmap_get (is_inserted, i)) + continue; + + mhash_unset (h, keys[i], 0); + mhash_unset (h, keys[i], 0); + is_inserted = clib_bitmap_xori (is_inserted, i); + + if (ht->n_iterations_per_validate == 0 || + (i + 1) % ht->n_iterations_per_validate) + continue; + + if ((error = hash_validate (h->hash))) + goto done; + + for (j = 0; j < vec_len (keys); j++) + { + uword *v; + v = mhash_get (h, keys[j]); + if ((error = CLIB_ERROR_ASSERT (clib_bitmap_get (is_inserted, j) == + (v != 0)))) + goto done; + if (v) + { + if ((error = CLIB_ERROR_ASSERT (v[0] == vals[j]))) + goto done; + } + } + } + +done: + mhash_free (h); + vec_free (vals); + clib_bitmap_free (is_inserted); + + for (i = 0; i < vec_len (keys); i++) + vec_free (keys[i]); + vec_free (keys); + + if (verbose) + fformat (stderr, "%U\n", format_clib_mem_usage, /* verbose */ 0); + + return error; +} + +int +test_mhash_main (unformat_input_t *input) +{ + mhash_test_t _ht = { 0 }, *ht = &_ht; + clib_error_t *error; + + ht->n_iterations = 100; + ht->n_pairs = 10; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (0 == unformat (input, "iter %d", &ht->n_iterations) && + 0 == unformat (input, "print %d", &ht->n_iterations_per_print) && + 0 == unformat (input, "elts %d", &ht->n_pairs) && + 0 == unformat (input, "seed %d", &ht->seed) && + 0 == unformat (input, "verbose %=", &ht->verbose, 1) && + 0 == unformat (input, "valid %d", &ht->n_iterations_per_validate)) + { + clib_warning ("unknown input `%U'", format_unformat_error, input); + return 1; + } + } + + if (!ht->seed) + ht->seed = random_default_seed (); + + if_verbose ("testing %d iterations, seed %d", ht->n_iterations, ht->seed); + + error = test_word_key (ht); + if (error) + clib_error_report (error); + + error = test_string_key (ht, 0); + if (error) + clib_error_report (error); + + error = test_string_key (ht, 1); + if (error) + clib_error_report (error); + + return 0; +} + +#ifdef CLIB_UNIX +int +main (int argc, char *argv[]) +{ + unformat_input_t i; + int ret; + + clib_mem_init (0, 3ULL << 30); + + verbose = (argc > 1); + unformat_init_command_line (&i, argv); + ret = test_mhash_main (&i); + unformat_free (&i); + + return ret; +} +#endif /* CLIB_UNIX */ diff --git a/src/vppinfra/unix-misc.c b/src/vppinfra/unix-misc.c index 29cbe0a557d..31c0a489e8d 100644 --- a/src/vppinfra/unix-misc.c +++ b/src/vppinfra/unix-misc.c @@ -35,6 +35,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include <vppinfra/error.h> #include <vppinfra/os.h> #include <vppinfra/bitmap.h> @@ -42,13 +46,19 @@ #include <vppinfra/format.h> #ifdef __linux__ #include <vppinfra/linux/sysfs.h> -#else +#include <sched.h> +#elif defined(__FreeBSD__) +#define _WANT_FREEBSD_BITSET +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/cpuset.h> +#include <sys/domainset.h> #include <sys/sysctl.h> #endif #include <sys/stat.h> #include <sys/types.h> -#include <sys/syscall.h> #include <sys/uio.h> /* writev */ #include <fcntl.h> #include <stdio.h> /* for sprintf */ @@ -290,7 +300,7 @@ os_get_cpu_affinity_bitmap (int pid) __CPU_ZERO_S (sizeof (cpu_set_t), &cpuset); - ret = syscall (SYS_sched_getaffinity, 0, sizeof (cpu_set_t), &cpuset); + ret = sched_getaffinity (0, sizeof (cpu_set_t), &cpuset); if (ret < 0) { @@ -302,8 +312,23 @@ os_get_cpu_affinity_bitmap (int pid) if (__CPU_ISSET_S (index, sizeof (cpu_set_t), &cpuset)) clib_bitmap_set (affinity_cpus, index, 1); return affinity_cpus; +#elif defined(__FreeBSD__) + cpuset_t mask; + uword *r = NULL; + + if (cpuset_getaffinity (CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET, -1, + sizeof (mask), &mask) != 0) + { + clib_bitmap_free (r); + return NULL; + } + + for (int bit = 0; bit < CPU_SETSIZE; bit++) + clib_bitmap_set (r, bit, CPU_ISSET (bit, &mask)); + + return r; #else - return 0; + return NULL; #endif } @@ -312,6 +337,21 @@ os_get_online_cpu_node_bitmap () { #if __linux__ return clib_sysfs_read_bitmap ("/sys/devices/system/node/online"); +#elif defined(__FreeBSD__) + domainset_t domain; + uword *r = NULL; + int policy; + + if (cpuset_getdomain (CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET, -1, + sizeof (domain), &domain, &policy) != 0) + { + clib_bitmap_free (r); + return NULL; + } + + for (int bit = 0; bit < CPU_SETSIZE; bit++) + clib_bitmap_set (r, bit, CPU_ISSET (bit, &domain)); + return r; #else return 0; #endif diff --git a/src/vppinfra/unix.h b/src/vppinfra/unix.h index abda21879f9..d0ddb93a46f 100644 --- a/src/vppinfra/unix.h +++ b/src/vppinfra/unix.h @@ -56,9 +56,6 @@ clib_error_t *unix_proc_file_contents (char *file, u8 ** result); /* Retrieve bitmap of online cpu cures */ clib_bitmap_t *os_get_online_cpu_core_bitmap (); -/* Retrieve bitmap of cpus vpp has affinity to */ -clib_bitmap_t *os_get_cpu_affinity_bitmap (int pid); - /* Retrieve bitmap of online cpu nodes (sockets) */ clib_bitmap_t *os_get_online_cpu_node_bitmap (); diff --git a/test/Makefile b/test/Makefile index 9b9cc178cf6..cabb3526c8c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -350,32 +350,72 @@ cov-prep: test-dep @lcov --zerocounters --directory $(VPP_BUILD_DIR) @test -z "$(EXTERN_COV_DIR)" || lcov --zerocounters --directory $(EXTERN_COV_DIR) +COV_REM_NOT_CODE="/usr/include/*" "*/build-root/*" "/opt/*" "/usr/lib/*" \ + "*_test.*" "*test_*" "*vat*" "*/vnet/unix/gdb_funcs.c" \ + "*pg.c" + +COV_REM_DRIVERS="*rdma*" "*/plugins/af_packet/*" "*/plugins/af_xdp/*" \ + "*/plugins/avf/*" "*/plugins/dma_intel/*" "*/vlib/pci/*" \ + "*/vnet/devices/*" "*/vlib/dma/*" "*/plugins/vmxnet3/*" \ + "*/vnet/devices/virtio/*" "*/plugins/perfmon/arm*" \ + "*/plugins/perfmon/intel/*" "*/vlib/vmbus/*" \ + "*/vnet/dev/*" "*/plugins/dev_ena/*" "*/plugins/dev_iavf/*" + +COV_REM_UNUSED_FEAT="*/plugins/ioam/analyse/*" "*/plugins/ioam/lib-*/*" \ + "*/plugins/ioam/export-common/*" "*/vnet/srp/*" \ + "*/lawful-intercept/*" "*/lisp/*" "*/vnet/osi/*" \ + "*/plugins/nsh/*" + +COV_REM_TODO_NO_TEST="*/vpp-api/client/*" "*/plugins/prom/*" \ + "*/plugins/tlspicotls/*" "*/plugins/tlsmbedtls/*" \ + "*/vppinfra/perfmon/*" "*/plugins/ila/*" \ + "*/vlib/linux/*" "*/vnet/util/radix.c" "*/vapi/vapi.hpp" \ + "*/vpp/api/types.c" "*/vpp/api/json_format.c" \ + "*/plugins/ioam/*/*.h" "*/linux/netns.c" "*/vnet/flow/*" \ + "*/vppinfra/random.c" "*/vppinfra/ring.h" \ + "*/vppinfra/bihash_vec8_8.h" "*/vppinfra/maplog.c" \ + "*/vppinfra/format_table.c" "*/vppinfra/timing_wheel.c" \ + "*/vppinfra/macros.c" "*/vppinfra/valloc.c" \ + "*/vppinfra/jsonformat.c" "*/vppinfra/vector/array_mask.h" \ + "*/vppinfra/vector/toeplitz.c" "*/plugins/vrrp/vrrp_packet.h" \ + "*/vnet/srv6/sr.h" "*/vlibapi/api_format.c" \ + "*/vlibapi/node_serialize.c" "*/plugins/quic/error.c" \ + "*/vnet/ipfix-export/flow_report_classify.h" \ + "*/vnet/ip/ip6_ll_types.c" "*/vnet/ip/ip_psh_cksum.h" \ + "*/vnet/ip/ip6_hop_by_hop.h" "*/vnet/ip/ip_format_fns.h" \ + "*/vnet/dpo/classify_dpo.h" "*/vnet/dpo/l3_proxy_dpo.h" \ + "*/vnet/ipsec/esp_format.c" "*/vnet/ethernet/sfp.c" \ + "*/vnet/ethernet/ethernet_format_fns.h" \ + "*/plugins/ikev2/ikev2_format.c" "*/vnet/bier/bier_types.c" + +COV_REM_ALT_TEST="*/plugins/hs_apps/*" "*/plugins/builtinurl/*" \ + "*/plugins/http/*.h" .PHONY: cov-post cov-post: wipe-cov $(BUILD_COV_DIR) @lcov --capture \ --directory $(VPP_BUILD_DIR) \ - --output-file $(BUILD_COV_DIR)/coverage.info + --output-file $(BUILD_COV_DIR)/coverage$(HS_TEST).info @test -z "$(EXTERN_COV_DIR)" || \ lcov --capture \ --directory $(EXTERN_COV_DIR) \ - --output-file $(BUILD_COV_DIR)/extern-coverage.info - @lcov --remove $(BUILD_COV_DIR)/coverage.info \ - "/usr/include/*" "*/build-root/*" "/opt/*" "/usr/lib/*" \ - "*_test.*" "*vat*" "*rdma*" "*/vpp-api/client/*" "*/plugins/af_packet/*" \ - "*/plugins/af_xdp/*" "*/plugins/avf/*" "*/plugins/dma_intel/*" \ - "*/plugins/hs_apps/*" "*/plugins/vmxnet3/*" "*/vnet/devices/virtio/*" \ - "*/plugins/perfmon/arm*" "*/plugins/perfmon/intel/*" "*/vlib/vmbus/*" \ - "*/vnet/dev/*" "*/plugins/dev_ena/*" "*/plugins/builtinurl/*" "*/vnet/flow/*" \ - "*/plugins/http_static/builtinurl/*" "*/plugins/dev_iavf/*" \ - -o $(BUILD_COV_DIR)/coverage-filtered.info - @genhtml $(BUILD_COV_DIR)/coverage-filtered.info \ + --output-file $(BUILD_COV_DIR)/extern-coverage$(HS_TEST).info + @lcov --remove $(BUILD_COV_DIR)/coverage$(HS_TEST).info \ + $(COV_REM_NOT_CODE) \ + $(COV_REM_DRIVERS) \ + $(COV_REM_TODO_NO_TEST) \ + $(COV_REM_UNUSED_FEAT) \ + $(COV_REM_ALT_TEST) \ + -o $(BUILD_COV_DIR)/coverage-filtered$(HS_TEST).info + @genhtml $(BUILD_COV_DIR)/coverage-filtered$(HS_TEST).info \ --output-directory $(BUILD_COV_DIR)/html @test -z "$(EXTERN_COV_DIR)" || \ - genhtml $(BUILD_COV_DIR)/extern-coverage.info \ + genhtml $(BUILD_COV_DIR)/extern-coverage$(HS_TEST).info \ --output-directory $(BUILD_COV_DIR)/extern-html @echo @echo "Build finished. Code coverage report is in $(BUILD_COV_DIR)/html/index.html" @test -z "$(EXTERN_COV_DIR)" || echo "Code coverage report for out-of-tree objects is in $(BUILD_COV_DIR)/extern-html/index.html" + @mkdir -p $(BR)/test-coverage-merged + @cp -f $(BUILD_COV_DIR)/coverage-filtered$(HS_TEST).info $(BR)/test-coverage-merged .PHONY: cov cov: @@ -434,6 +474,7 @@ help: @echo " test-cov-prep - coverage phase #1 : prepare lcov" @echo " test-cov-build - coverage phase #2 : build gcov image & run tests against it (use TEST=)" @echo " test-cov-post - coverage phase #3 : generate lcov html report" + @echo " test-cov-both - generate and merge code coverage report for Python and Golang tests" @echo " test-all - build and run functional and extended tests" @echo " test-all-debug - build and run functional and extended tests (debug build)" @echo " test-all-cov - generate code coverage report for functional and extended tests" diff --git a/test/scripts/core_pinning.sh b/test/scripts/core_pinning.sh deleted file mode 100755 index 941d53871e5..00000000000 --- a/test/scripts/core_pinning.sh +++ /dev/null @@ -1,328 +0,0 @@ -#!/bin/bash -# -# core_pinning_auto.sh -- script to test vpp (debug-build) core-pinning -# -- in bare-metal, containers (docker, lxc) -# -DOCKER_CONTAINER_NAME="vpp_core_pinning" -VPP_SOCK_PATH=/run/vpp -CONTAINER_CPU_RANGE="4-7" -TEST_SUCCESS=0 -TEST_FAIL=0 -if [ ! $WS_ROOT ] -then - if [ ! -d "../../../vpp" ]; then - echo "VPP workspace path invalid" - echo "Please execute script from vpp/test/scripts folder.." - exit 1 - fi - WS_ROOT="$(dirname $(readlink -e "../../../vpp"))/$(basename "../../../vpp")" -fi -# Get available CPU count on host machine -host_cpulist=$(cat /sys/devices/system/cpu/online) -startcpu="${host_cpulist%-*}" -endcpu="${host_cpulist#*\-}" -cpucount="$(($endcpu - $startcpu + 1))" -if [ $cpucount -lt 8 ] -then - echo "Current host machine has $cpucount CPUs" - echo "A minimum of 8 CPUs is required to run testcases, exiting.." - exit 1 -fi -# Check that container 'vpp_core_pinning' does not already exist -count=$(docker ps -a | grep -c "$DOCKER_CONTAINER_NAME") -if [ $count -ne 0 ] -then - echo "Error: docker container $DOCKER_CONTAINER_NAME already exists" - echo "Remove it using 'docker stop/docker rm', then re-run test" - exit 1 -fi -# Check that there is no vpp instance currently running on the machine -count=$(pgrep vpp | wc -l) -if [ $count -ne 0 ] -then - echo "Error: a vpp instance is currently running on this machine" - echo "Please stop the running instance, then re-run test" - exit 1 -fi -mkdir -p $VPP_SOCK_PATH - -# Function to parse main core -parse_maincore () { - main_core_args=$1 - main_core_parsed=$main_core_args - if [ $main_core_args = "auto" ]; - then - main_core_parsed="0" - if [ -n "$SKIP_CORE" ] - then - main_core_parsed=$(($main_core_parsed + $SKIP_CORE)) - fi - if [ -n "$CONTAINER_RESTRAIN_CPUSET" ] - then - main_core_parsed=(${container_cpus[ $main_core_parsed ]}) - fi - fi - echo $main_core_parsed -} - -# Function to parse n workers range to an array -# e.g. "4" is parsed to ('0','1','2','3') -parse_workers_n () { - workers_n_args=$1 - workers_n_parsed=() - main_core_increment="0" - skip_core_increment="0" - if [ -n "$SKIP_CORE" ] - then - skip_core_increment=$(($SKIP_CORE)) - fi - - for ((i=0;i<$workers_n_args;i++)); do - - if [ -n "$CONTAINER_RESTRAIN_CPUSET" ] - then - if [ $(( ${container_cpus[ $(($i + $skip_core_increment)) ]})) -eq $(("$parsed_main_core")) ] - then - main_core_increment=$(($main_core_increment + 1)) - fi - workers_n_parsed+=" ${container_cpus[ $(($i + $main_core_increment + $skip_core_increment)) ]}" - else - if [ $(( $skip_core_increment + $i)) -eq $(("$parsed_main_core")) ] - then - main_core_increment=$(($main_core_increment + 1)) - fi - workers_n_parsed+=" $(($i + $main_core_increment + $skip_core_increment))" - fi - done - echo $workers_n_parsed -} - -# Function to parse corelist range to an array -# e.g. "0,3-5,7" is parsed to ('0','3','4','5','7') -parse_corelist () { - corelist_args=$1 - corelist_args=$(echo $corelist_args | grep -Po '[0-9]+-[0-9]+|[0-9]+') - corelist_parsed=() - for corelist_elt in ${corelist_args[@]};do - if [ $(echo $corelist_elt | grep -Po '[0-9]+-[0-9]+') ] - then - startcpu="${corelist_elt%-*}" - endcpu="${corelist_elt#*\-}" - cpucount="$(($endcpu - $startcpu))" - for ((i=0;i<=$cpucount;i++)); do - corelist_parsed+=" $(($i+$startcpu))" - done - elif [ $(echo $corelist_elt | grep -Po '[0-9]+') ] - then - corelist_parsed+=" ${corelist_elt}" - fi - done - echo $corelist_parsed -} -# Test VPP core pinning configuration -test_pinning_conf () { - VPP_CPU_EXTRA_OPTIONS="" - if [ -n "$CORELIST_WORKERS" ]; - then - VPP_CPU_EXTRA_OPTIONS=" corelist-workers ${CORELIST_WORKERS}" - fi - if [ -n "$WORKERS_AUTO" ]; - then - VPP_CPU_EXTRA_OPTIONS=" workers ${WORKERS_AUTO}" - fi - if [ -n "$SKIP_CORE" ]; - then - VPP_CPU_EXTRA_OPTIONS="${VPP_CPU_EXTRA_OPTIONS} skip-cores ${SKIP_CORE}" - fi - echo "TEST - conf 'cpu {main-core ${MAIN_CORE} ${VPP_CPU_EXTRA_OPTIONS}}'" - if [ -z "$CONTAINER_RESTRAIN_CPUSET" ]; - then - VPP_CONTAINER_CPUSET="" - echo "(Running vpp in container with full host cpuset $host_cpulist)" - else - VPP_CONTAINER_CPUSET="--cpuset-cpus $CONTAINER_CPU_RANGE" - echo "(Running vpp in container with limited cpuset $CONTAINER_CPU_RANGE)" - fi - (docker run -d ${VPP_CONTAINER_CPUSET} --name="$DOCKER_CONTAINER_NAME" \ - -e LD_LIBRARY_PATH="/vpp/build-root/build-vpp_debug-native/vpp/lib/x86_64-linux-gnu/" -v $VPP_SOCK_PATH:$VPP_SOCK_PATH \ - -v $WS_ROOT:/vpp ubuntu:22.04 sh -c "/vpp/build-root/build-vpp_debug-native/vpp/bin/vpp unix {interactive \ - nodaemon cli-listen $VPP_SOCK_PATH/cli.sock} cpu {main-core ${MAIN_CORE} ${VPP_CPU_EXTRA_OPTIONS} } plugins \ - { plugin dpdk_plugin.so {disable } }" > /dev/null ) - sleep 3 # wait for VPP to initialize socket - # Change access permissions on vpp cli socket - # docker exec -it "$DOCKER_CONTAINER_NAME" /bin/bash -c "chmod 777 $VPP_SOCK_PATH/cli.sock" > /dev/null - # check if vppctl can connect to vpp container instance - $WS_ROOT/build-root/build-vpp_debug-native/vpp/bin/vppctl -s $VPP_SOCK_PATH/cli.sock show threads 1> /dev/null - # get CPUs vpp instance in container is running on - taskset_vpp_cpus=($( taskset --all-tasks -pc $(pgrep vpp) | grep -e ".$" -o)) - rc=$? - # parse list of user requested CPUs for vpp - requested_cpus=() - parsed_main_core=$(parse_maincore ${MAIN_CORE}) - requested_cpus+=($parsed_main_core) - if [ -n "$CORELIST_WORKERS" ]; - then - requested_cpus+=($(parse_corelist ${CORELIST_WORKERS})) - fi - if [ -n "$WORKERS_AUTO" ]; - then - requested_cpus+=($(parse_workers_n ${WORKERS_AUTO})) - fi - - # parse list of expected CPUs used by vpp - expected_cpu_mapping=() - expected_cpu_mapping=("${requested_cpus[@]}") - echo "CPUs requested by user: [${requested_cpus[@]}]" - echo "--------------------" - echo "Expected CPU Mapping: [${expected_cpu_mapping[@]}]" - echo "VPP pinning (taskset): [${taskset_vpp_cpus[@]}]" - #check if expected CPU mapping matches CPUs vpp instance in container is running on - failure_cond="" - for index in ${!taskset_vpp_cpus[@]}; do - if [ ${taskset_vpp_cpus[$index]} -ne ${expected_cpu_mapping[ $index ]} ] - then - failure_cond="t" - fi - done - if [ $rc -eq 0 ] && [ -z "$failure_cond" ] - then - echo "Test Successful" - TEST_SUCCESS=$(($TEST_SUCCESS+1)) - else - echo "Test Failed" - TEST_FAIL=$(($TEST_FAIL+1)) - fi - echo "==============================================" - echo " " - # Stop & destroy container instance - docker stop $DOCKER_CONTAINER_NAME &> /dev/null - docker rm -f $DOCKER_CONTAINER_NAME &> /dev/null -} -test_invalid_conf () { - if [ -n "$CORELIST_WORKERS" ]; - then - VPP_CPU_EXTRA_OPTIONS=" corelist-workers ${CORELIST_WORKERS}" - fi - if [ -n "$WORKERS_AUTO" ]; - then - VPP_CPU_EXTRA_OPTIONS=" workers ${WORKERS_AUTO}" - fi - if [ -n "$SKIP_CORE" ]; - then - VPP_CPU_EXTRA_OPTIONS="${VPP_CPU_EXTRA_OPTIONS} skip-cores ${SKIP_CORE}" - fi - echo "TEST - conf 'cpu {main-core ${MAIN_CORE} ${VPP_CPU_EXTRA_OPTIONS}}'" - if [ -z "$CONTAINER_RESTRAIN_CPUSET" ]; - then - VPP_CONTAINER_CPUSET="" - echo "(Running vpp in container with full host cpuset $host_cpulist)" - else - VPP_CONTAINER_CPUSET="--cpuset-cpus $CONTAINER_CPU_RANGE" - echo "(Running vpp in container with limited cpuset $CONTAINER_CPU_RANGE)" - fi - (docker run -d --cpuset-cpus $CONTAINER_CPU_RANGE --name="$DOCKER_CONTAINER_NAME" \ - -e LD_LIBRARY_PATH="/vpp/build-root/build-vpp_debug-native/vpp/lib/x86_64-linux-gnu/" -v $VPP_SOCK_PATH:$VPP_SOCK_PATH \ - -v $WS_ROOT:/vpp ubuntu:22.04 sh -c "/vpp/build-root/build-vpp_debug-native/vpp/bin/vpp unix {interactive \ - nodaemon cli-listen $VPP_SOCK_PATH/cli.sock} cpu {main-core ${MAIN_CORE} ${VPP_CPU_EXTRA_OPTIONS}} plugins \ - { plugin dpdk_plugin.so {disable } }" > /dev/null) - sleep 3 # wait for vpp to initialize socket - # check if vpp launched with invalid configuration - taskset --all-tasks -pc $(pgrep vpp) &> /dev/null - rc=$? - if [ $rc -eq 1 ] - then - echo " " - echo "OK... VPP did not launch with invalid configuration" - TEST_SUCCESS=$(($TEST_SUCCESS+1)) - else - echo " " - echo "Failure... VPP launched with wrong configuration" - TEST_FAIL=$(($TEST_FAIL+1)) - fi - echo "==============================================" - echo " " - # Stop & destroy container instance - docker stop $DOCKER_CONTAINER_NAME &> /dev/null - docker rm -f $DOCKER_CONTAINER_NAME &> /dev/null -} -run_tests () { - container_cpus=($(parse_corelist ${CONTAINER_CPU_RANGE})) - echo "TESTING VALID CORE PINNING CONFIGURATIONS" - echo " " - WORKERS_AUTO="" - SKIP_CORE="" - CONTAINER_RESTRAIN_CPUSET="" - CORELIST_WORKERS="1-3" - MAIN_CORE="0" - test_pinning_conf - WORKERS_AUTO="" - SKIP_CORE="" - CONTAINER_RESTRAIN_CPUSET="" - CORELIST_WORKERS="0,2-3" - MAIN_CORE="1" - test_pinning_conf - WORKERS_AUTO="" - SKIP_CORE="" - CONTAINER_RESTRAIN_CPUSET="" - CORELIST_WORKERS="0-2" - MAIN_CORE="3" - test_pinning_conf - WORKERS_AUTO="2" - SKIP_CORE="" - CONTAINER_RESTRAIN_CPUSET="" - CORELIST_WORKERS="" - MAIN_CORE="auto" - test_pinning_conf - WORKERS_AUTO="3" - SKIP_CORE="" - CONTAINER_RESTRAIN_CPUSET="t" - CORELIST_WORKERS="" - MAIN_CORE="auto" - test_pinning_conf - WORKERS_AUTO="2" - SKIP_CORE="1" - CONTAINER_RESTRAIN_CPUSET="t" - CORELIST_WORKERS="" - MAIN_CORE="auto" - test_pinning_conf - WORKERS_AUTO="2" - SKIP_CORE="" - CONTAINER_RESTRAIN_CPUSET="t" - CORELIST_WORKERS="" - MAIN_CORE="5" - test_pinning_conf - echo "TESTING NON-VALID CORE PINNING CONFIGURATIONS" - echo " " - WORKERS_AUTO="" - SKIP_CORE="" - CONTAINER_RESTRAIN_CPUSET="t" - CORELIST_WORKERS="1-3" - MAIN_CORE="0" - test_invalid_conf - WORKERS_AUTO="3" - SKIP_CORE="1" - CONTAINER_RESTRAIN_CPUSET="t" - CORELIST_WORKERS="" - MAIN_CORE="auto" - test_invalid_conf - WORKERS_AUTO="5" - SKIP_CORE="" - CONTAINER_RESTRAIN_CPUSET="t" - CORELIST_WORKERS="" - MAIN_CORE="auto" - test_invalid_conf - WORKERS_AUTO="" - SKIP_CORE="4" - CONTAINER_RESTRAIN_CPUSET="t" - CORELIST_WORKERS="" - MAIN_CORE="auto" - test_invalid_conf - echo " " - echo "========================" - echo "RESULTS:" - echo "SUCCESS: $TEST_SUCCESS" - echo "FAILURE: $TEST_FAIL" - echo "========================" - echo " " -} -run_tests
\ No newline at end of file diff --git a/test/template_ipsec.py b/test/template_ipsec.py index b5cd922f127..7953f603287 100644 --- a/test/template_ipsec.py +++ b/test/template_ipsec.py @@ -3022,7 +3022,7 @@ class SpdFlowCacheTemplate(IPSecIPv4Fwd): return False def create_stream( - cls, src_if, dst_if, pkt_count, src_prt=1234, dst_prt=5678, proto="UDP-ESP" + cls, src_if, dst_if, pkt_count, src_prt=1234, dst_prt=4500, proto="UDP-ESP" ): packets = [] packets = super(SpdFlowCacheTemplate, cls).create_stream( @@ -3031,7 +3031,7 @@ class SpdFlowCacheTemplate(IPSecIPv4Fwd): return packets def verify_capture( - self, src_if, dst_if, capture, tcp_port_in=1234, udp_port_in=5678 + self, src_if, dst_if, capture, tcp_port_in=1234, udp_port_in=4500 ): super(SpdFlowCacheTemplate, self).verify_l3_l4_capture( src_if, dst_if, capture, tcp_port_in, udp_port_in @@ -3056,7 +3056,7 @@ class SpdFastPathTemplate(IPSecIPv4Fwd): super(SpdFastPathTemplate, self).tearDown() def create_stream( - cls, src_if, dst_if, pkt_count, src_prt=1234, dst_prt=5678, proto="UDP-ESP" + cls, src_if, dst_if, pkt_count, src_prt=1234, dst_prt=4500, proto="UDP-ESP" ): packets = [] packets = super(SpdFastPathTemplate, cls).create_stream( @@ -3065,7 +3065,7 @@ class SpdFastPathTemplate(IPSecIPv4Fwd): return packets def verify_capture( - self, src_if, dst_if, capture, tcp_port_in=1234, udp_port_in=5678 + self, src_if, dst_if, capture, tcp_port_in=1234, udp_port_in=4500 ): super(SpdFastPathTemplate, self).verify_l3_l4_capture( src_if, dst_if, capture, tcp_port_in, udp_port_in @@ -3084,7 +3084,7 @@ class IpsecDefaultTemplate(IPSecIPv4Fwd): super(IpsecDefaultTemplate, self).tearDown() def create_stream( - cls, src_if, dst_if, pkt_count, src_prt=1234, dst_prt=5678, proto="UDP-ESP" + cls, src_if, dst_if, pkt_count, src_prt=1234, dst_prt=4500, proto="UDP-ESP" ): packets = [] packets = super(IpsecDefaultTemplate, cls).create_stream( @@ -3093,7 +3093,7 @@ class IpsecDefaultTemplate(IPSecIPv4Fwd): return packets def verify_capture( - self, src_if, dst_if, capture, tcp_port_in=1234, udp_port_in=5678 + self, src_if, dst_if, capture, tcp_port_in=1234, udp_port_in=4500 ): super(IpsecDefaultTemplate, self).verify_l3_l4_capture( src_if, dst_if, capture, tcp_port_in, udp_port_in diff --git a/test/test_abf.py b/test/test_abf.py index 3baec9f9e9b..ec329a0ec9b 100644 --- a/test/test_abf.py +++ b/test/test_abf.py @@ -391,7 +391,7 @@ class TestAbf(VppTestCase): # a packet matching the deny rule # p_deny = ( - Ether(src=self.pg0.remote_mac, dst=self.pg3.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg3.remote_ip4) / UDP(sport=1234, dport=1234) / Raw(b"\xa5" * 100) @@ -402,7 +402,7 @@ class TestAbf(VppTestCase): # a packet matching the permit rule # p_permit = ( - Ether(src=self.pg0.remote_mac, dst=self.pg2.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg2.remote_ip4) / UDP(sport=1234, dport=1234) / Raw(b"\xa5" * 100) @@ -454,7 +454,7 @@ class TestAbf(VppTestCase): # a packet matching the deny rule # p_deny = ( - Ether(src=self.pg0.remote_mac, dst=self.pg3.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IPv6(src=self.pg0.remote_ip6, dst=self.pg3.remote_ip6) / UDP(sport=1234, dport=1234) / Raw(b"\xa5" * 100) @@ -465,7 +465,7 @@ class TestAbf(VppTestCase): # a packet matching the permit rule # p_permit = ( - Ether(src=self.pg0.remote_mac, dst=self.pg2.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IPv6(src=self.pg0.remote_ip6, dst=self.pg2.remote_ip6) / UDP(sport=1234, dport=1234) / Raw(b"\xa5" * 100) diff --git a/test/test_flowprobe.py b/test/test_flowprobe.py index ac0433abc00..8e3fecfd7b4 100644 --- a/test/test_flowprobe.py +++ b/test/test_flowprobe.py @@ -559,7 +559,7 @@ class Flowprobe(MethodHolder): # make a tcp packet self.pkts = [ ( - Ether(src=self.pg3.remote_mac, dst=self.pg4.local_mac) + Ether(src=self.pg3.remote_mac, dst=self.pg3.local_mac) / IP(src=self.pg3.remote_ip4, dst=self.pg4.remote_ip4) / TCP(sport=1234, dport=4321) / Raw(b"\xa5" * 50) diff --git a/test/test_gso.py b/test/test_gso.py index 78c5c734660..3d9ce5fb4ee 100644 --- a/test/test_gso.py +++ b/test/test_gso.py @@ -405,7 +405,7 @@ class TestGSO(VppTestCase): # IPv4/IPv4 - VXLAN # p45 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst="172.16.3.3", flags="DF") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -419,12 +419,12 @@ class TestGSO(VppTestCase): self.assertEqual(rx[IP].src, self.pg0.local_ip4) self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) self.assert_ip_checksum_valid(rx) - self.assert_udp_checksum_valid(rx, ignore_zero_checksum=False) + self.assert_udp_checksum_valid(rx, ignore_zero_checksum=True) self.assertEqual(rx[VXLAN].vni, 10) inner = rx[VXLAN].payload self.assertEqual(rx[IP].len - 20 - 8 - 8, len(inner)) self.assertEqual(inner[Ether].src, self.pg2.remote_mac) - self.assertEqual(inner[Ether].dst, "02:fe:60:1e:a2:79") + self.assertEqual(inner[Ether].dst, self.pg2.local_mac) self.assertEqual(inner[IP].src, self.pg2.remote_ip4) self.assertEqual(inner[IP].dst, "172.16.3.3") self.assert_ip_checksum_valid(inner) @@ -438,7 +438,7 @@ class TestGSO(VppTestCase): # IPv4/IPv6 - VXLAN # p65 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IPv6(src=self.pg2.remote_ip6, dst="fd01:3::3") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -452,12 +452,12 @@ class TestGSO(VppTestCase): self.assertEqual(rx[IP].src, self.pg0.local_ip4) self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) self.assert_ip_checksum_valid(rx) - self.assert_udp_checksum_valid(rx, ignore_zero_checksum=False) + self.assert_udp_checksum_valid(rx, ignore_zero_checksum=True) self.assertEqual(rx[VXLAN].vni, 10) inner = rx[VXLAN].payload self.assertEqual(rx[IP].len - 20 - 8 - 8, len(inner)) self.assertEqual(inner[Ether].src, self.pg2.remote_mac) - self.assertEqual(inner[Ether].dst, "02:fe:60:1e:a2:79") + self.assertEqual(inner[Ether].dst, self.pg2.local_mac) self.assertEqual(inner[IPv6].src, self.pg2.remote_ip6) self.assertEqual(inner[IPv6].dst, "fd01:3::3") self.assert_tcp_checksum_valid(inner) @@ -483,7 +483,7 @@ class TestGSO(VppTestCase): # IPv6/IPv4 - VXLAN # p46 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst="172.16.3.3", flags="DF") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -501,7 +501,7 @@ class TestGSO(VppTestCase): inner = rx[VXLAN].payload self.assertEqual(rx[IPv6].plen - 8 - 8, len(inner)) self.assertEqual(inner[Ether].src, self.pg2.remote_mac) - self.assertEqual(inner[Ether].dst, "02:fe:60:1e:a2:79") + self.assertEqual(inner[Ether].dst, self.pg2.local_mac) self.assertEqual(inner[IP].src, self.pg2.remote_ip4) self.assertEqual(inner[IP].dst, "172.16.3.3") self.assert_ip_checksum_valid(inner) @@ -515,7 +515,7 @@ class TestGSO(VppTestCase): # IPv6/IPv6 - VXLAN # p66 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IPv6(src=self.pg2.remote_ip6, dst="fd01:3::3") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -533,7 +533,7 @@ class TestGSO(VppTestCase): inner = rx[VXLAN].payload self.assertEqual(rx[IPv6].plen - 8 - 8, len(inner)) self.assertEqual(inner[Ether].src, self.pg2.remote_mac) - self.assertEqual(inner[Ether].dst, "02:fe:60:1e:a2:79") + self.assertEqual(inner[Ether].dst, self.pg2.local_mac) self.assertEqual(inner[IPv6].src, self.pg2.remote_ip6) self.assertEqual(inner[IPv6].dst, "fd01:3::3") self.assert_tcp_checksum_valid(inner) @@ -590,7 +590,7 @@ class TestGSO(VppTestCase): # IPv4/IPv4 - IPIP # p47 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst="172.16.10.3", flags="DF") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -633,7 +633,7 @@ class TestGSO(VppTestCase): # IPv4/IPv6 - IPIP # p67 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IPv6(src=self.pg2.remote_ip6, dst="fd01:10::3") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -731,7 +731,7 @@ class TestGSO(VppTestCase): # IPv6/IPv4 - IPIP # p48 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst="172.16.10.3", flags="DF") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -774,7 +774,7 @@ class TestGSO(VppTestCase): # IPv6/IPv6 - IPIP # p68 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IPv6(src=self.pg2.remote_ip6, dst="fd01:10::3") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -842,7 +842,7 @@ class TestGSO(VppTestCase): self.ip4_via_gre4_tunnel.add_vpp_config() pgre4 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst="172.16.10.3", flags="DF") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -952,7 +952,7 @@ class TestGSO(VppTestCase): # Create IPv6 packet # pgre6 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IPv6(src=self.pg2.remote_ip6, dst="fd01:10::3") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -1078,7 +1078,7 @@ class TestGSO(VppTestCase): # IPv4/IPv4 - IPSEC # ipsec44 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst="172.16.10.3", flags="DF") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -1116,7 +1116,7 @@ class TestGSO(VppTestCase): # IPv4/IPv6 - IPSEC # ipsec46 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IPv6(src=self.pg2.remote_ip6, dst="fd01:10::3") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -1213,7 +1213,7 @@ class TestGSO(VppTestCase): # IPv6/IPv4 - IPSEC # ipsec64 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst="172.16.10.3", flags="DF") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) @@ -1252,7 +1252,7 @@ class TestGSO(VppTestCase): # IPv6/IPv6 - IPSEC # ipsec66 = ( - Ether(src=self.pg2.remote_mac, dst="02:fe:60:1e:a2:79") + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IPv6(src=self.pg2.remote_ip6, dst="fd01:10::3") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) diff --git a/test/test_gtpu.py b/test/test_gtpu.py index fd97205ac63..5fe4f36ccb3 100644 --- a/test/test_gtpu.py +++ b/test/test_gtpu.py @@ -36,7 +36,7 @@ class TestGtpuUDP(VppTestCase): def _check_udp_port_ip4(self, enabled=True): pkt = ( - Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) / UDP(sport=self.dport, dport=self.dport, chksum=0) ) @@ -55,7 +55,7 @@ class TestGtpuUDP(VppTestCase): def _check_udp_port_ip6(self, enabled=True): pkt = ( - Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) / UDP(sport=self.dport, dport=self.dport, chksum=0) ) diff --git a/test/test_ip4.py b/test/test_ip4.py index 926ca77a5f8..a183b0ca0ba 100644 --- a/test/test_ip4.py +++ b/test/test_ip4.py @@ -244,12 +244,13 @@ class TestIPv4RouteLookup(VppTestCase): """IPv4 Route Lookup Test Case""" routes = [] + tables = [] - def route_lookup(self, prefix, exact): + def route_lookup(self, prefix, exact, table_id=0): return self.vapi.api( self.vapi.papi.ip_route_lookup, { - "table_id": 0, + "table_id": table_id, "exact": exact, "prefix": prefix, }, @@ -283,11 +284,30 @@ class TestIPv4RouteLookup(VppTestCase): r.add_vpp_config() self.routes.append(r) + custom_vrf = VppIpTable(self, 200) + custom_vrf.add_vpp_config() + self.tables.append(custom_vrf) + + r = VppIpRoute(self, "2.2.0.0", 16, [drop_nh], 200) + r.add_vpp_config() + self.routes.append(r) + + r = VppIpRoute(self, "2.2.2.0", 24, [drop_nh], 200) + r.add_vpp_config() + self.routes.append(r) + + r = VppIpRoute(self, "2.2.2.2", 32, [drop_nh], 200) + r.add_vpp_config() + self.routes.append(r) + def tearDown(self): # Remove the routes we added for r in self.routes: r.remove_vpp_config() + for vrf in self.tables: + vrf.remove_vpp_config() + super(TestIPv4RouteLookup, self).tearDown() def test_exact_match(self): @@ -305,6 +325,20 @@ class TestIPv4RouteLookup(VppTestCase): with self.vapi.assert_negative_api_retval(): self.route_lookup("1.1.1.2/32", True) + # Verify we find the host route + prefix = "2.2.2.2/32" + result = self.route_lookup(prefix, True, 200) + assert prefix == str(result.route.prefix) + + # Verify we find a middle prefix route + prefix = "2.2.2.0/24" + result = self.route_lookup(prefix, True, 200) + assert prefix == str(result.route.prefix) + + # Verify we do not find an available LPM. + with self.vapi.assert_negative_api_retval(): + self.route_lookup("2.2.2.1/32", True, 200) + def test_longest_prefix_match(self): # verify we find lpm lpm_prefix = "1.1.1.0/24" @@ -315,6 +349,15 @@ class TestIPv4RouteLookup(VppTestCase): result = self.route_lookup(lpm_prefix, False) assert lpm_prefix == str(result.route.prefix) + # verify we find lpm + lpm_prefix = "2.2.2.0/24" + result = self.route_lookup("2.2.2.1/32", False, 200) + assert lpm_prefix == str(result.route.prefix) + + # Verify we find the exact when not requested + result = self.route_lookup(lpm_prefix, False, 200) + assert lpm_prefix == str(result.route.prefix) + # Can't seem to delete the default route so no negative LPM test. diff --git a/test/test_ip6.py b/test/test_ip6.py index a1cd943570c..84b060aa7a3 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -3409,9 +3409,11 @@ class TestIP6LinkLocal(VppTestCase): # Use the specific link-local API on pg1 # VppIp6LinkLocalAddress(self, self.pg1, ll1).add_vpp_config() + p_echo_request_1.dst = self.pg1.local_mac self.send_and_expect(self.pg1, [p_echo_request_1], self.pg1) VppIp6LinkLocalAddress(self, self.pg1, ll3).add_vpp_config() + p_echo_request_3.dst = self.pg1.local_mac self.send_and_expect(self.pg1, [p_echo_request_3], self.pg1) def test_ip6_ll_p2p(self): diff --git a/test/test_ip6_nd_mirror_proxy.py b/test/test_ip6_nd_mirror_proxy.py index 65209925e87..10dc77e1a86 100644 --- a/test/test_ip6_nd_mirror_proxy.py +++ b/test/test_ip6_nd_mirror_proxy.py @@ -161,7 +161,7 @@ class TestNDPROXY(VppTestCase): redirect.add_vpp_config() echo_reply = ( - Ether(dst=self.pg0.remote_mac, src=self.pg0.local_mac) + Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) / ICMPv6EchoReply(seq=1, id=id) ) diff --git a/test/test_ipip.py b/test/test_ipip.py index 2817d5a17ba..9574c2c299c 100644 --- a/test/test_ipip.py +++ b/test/test_ipip.py @@ -677,7 +677,7 @@ class TestIPIP(VppTestCase): / Raw(b"0x44" * 100) ) tx_e = [ - (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / inner) + (Ether(dst=self.pg2.local_mac, src=self.pg0.remote_mac) / inner) for x in range(63) ] @@ -1454,7 +1454,7 @@ class TestIPIPMPLS(VppTestCase): # Tunnel Decap # p4 = ( - self.p_ether + Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4) / MPLS(label=44, ttl=4) / IP(src="1.1.1.1", dst="2.2.2.2") @@ -1468,7 +1468,7 @@ class TestIPIPMPLS(VppTestCase): self.assertEqual(rx[IP].dst, "2.2.2.2") p6 = ( - self.p_ether + Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / IPv6(src=self.pg1.remote_ip6, dst=self.pg1.local_ip6) / MPLS(label=66, ttl=4) / IPv6(src="1::1", dst="2::2") diff --git a/test/test_ipsec_spd_flow_cache_input.py b/test/test_ipsec_spd_flow_cache_input.py index 283f345be18..b913a980599 100644 --- a/test/test_ipsec_spd_flow_cache_input.py +++ b/test/test_ipsec_spd_flow_cache_input.py @@ -785,9 +785,9 @@ class IPSec4SpdTestCaseCollisionInbound(SpdFlowCacheInbound): # create the packet streams # packet hashes to: # ad727628 - packets1 = self.create_stream(self.pg2, self.pg1, pkt_count, 1, 1) + packets1 = self.create_stream(self.pg2, self.pg1, pkt_count, 1, 4500) # b5512898 - packets2 = self.create_stream(self.pg0, self.pg3, pkt_count, 1, 1) + packets2 = self.create_stream(self.pg0, self.pg3, pkt_count, 1, 4500) # add the streams to the source interfaces self.pg2.add_stream(packets1) self.pg0.add_stream(packets2) @@ -821,9 +821,9 @@ class IPSec4SpdTestCaseCollisionInbound(SpdFlowCacheInbound): # create the packet streams # 2f8f90f557eef12c - packets1 = self.create_stream(self.pg2, self.pg1, pkt_count, 1, 1) + packets1 = self.create_stream(self.pg2, self.pg1, pkt_count, 1, 4500) # 6b7f9987719ffc1c - packets2 = self.create_stream(self.pg3, self.pg2, pkt_count, 1, 1) + packets2 = self.create_stream(self.pg3, self.pg2, pkt_count, 1, 4500) # add the streams to the source interfaces self.pg2.add_stream(packets1) self.pg3.add_stream(packets2) diff --git a/test/test_l3xc.py b/test/test_l3xc.py index 351c599051c..69267b35817 100644 --- a/test/test_l3xc.py +++ b/test/test_l3xc.py @@ -126,7 +126,7 @@ class TestL3xc(VppTestCase): p_2 = [] for ii in range(NUM_PKTS): p_2.append( - Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) + Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src="1.1.1.1", dst="1.1.1.2") / UDP(sport=1000 + ii, dport=1234) / Raw(b"\xa5" * 100) diff --git a/test/test_linux_cp.py b/test/test_linux_cp.py index a9ff242e215..95a9f1aca1f 100644 --- a/test/test_linux_cp.py +++ b/test/test_linux_cp.py @@ -126,7 +126,7 @@ class TestLinuxCP(VppTestCase): for phy, host in zip(phys, hosts): for j in range(N_HOSTS): p = ( - Ether(src=phy.local_mac, dst=phy.remote_hosts[j].mac) + Ether(src=phy.local_mac, dst=host.local_mac) / IP(src=phy.local_ip4, dst=phy.remote_hosts[j].ip4) / UDP(sport=1234, dport=1234) / Raw() diff --git a/test/test_map.py b/test/test_map.py index 565f7da6491..c65c5df7cda 100644 --- a/test/test_map.py +++ b/test/test_map.py @@ -494,7 +494,7 @@ class TestMAP(VppTestCase): # # Send a v4 packet that will be encapped. # - p_ether = Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) + p_ether = Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) p_ip4 = IP(src=self.pg0.remote_ip4, dst="192.168.1.1") p_tcp = TCP(sport=20000, dport=30000, flags="S", options=[("MSS", 1455)]) p4 = p_ether / p_ip4 / p_tcp diff --git a/test/test_map_br.py b/test/test_map_br.py index 0de0e31520c..a2c00d8d8ea 100644 --- a/test/test_map_br.py +++ b/test/test_map_br.py @@ -280,7 +280,7 @@ class TestMAPBR(VppTestCase): def test_map_t_echo_request_ip4_to_ip6(self): """MAP-T echo request IPv4 -> IPv6""" - eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) + eth = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) ip = IP(src=self.pg0.remote_ip4, dst=self.ipv4_map_address) icmp = ICMP(type="echo-request", id=self.ipv6_udp_or_tcp_map_port) payload = "H" * 10 @@ -306,7 +306,7 @@ class TestMAPBR(VppTestCase): def test_map_t_echo_reply_ip4_to_ip6(self): """MAP-T echo reply IPv4 -> IPv6""" - eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) + eth = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) ip = IP(src=self.pg0.remote_ip4, dst=self.ipv4_map_address) icmp = ICMP(type="echo-reply", id=self.ipv6_udp_or_tcp_map_port) payload = "H" * 10 diff --git a/test/test_mpls.py b/test/test_mpls.py index 9c07251a481..0e747ec7cd0 100644 --- a/test/test_mpls.py +++ b/test/test_mpls.py @@ -2018,7 +2018,7 @@ class TestMPLS(VppTestCase): binding.add_vpp_config() tx = ( - Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / MPLS(label=44, ttl=64) / IP(src=self.pg0.remote_ip4, dst=self.pg0.remote_ip4) / UDP(sport=1234, dport=1234) diff --git a/test/test_nat44_ed_output.py b/test/test_nat44_ed_output.py index ad1c5611418..dbf1dc40a75 100644 --- a/test/test_nat44_ed_output.py +++ b/test/test_nat44_ed_output.py @@ -216,7 +216,7 @@ class TestNAT44EDOutput(VppTestCase): # send FIN+ACK packet in->out - will cause session to be wiped # but won't create a new session p = ( - Ether(src=pg0.remote_mac, dst=pg0.local_mac) + Ether(src=pg1.remote_mac, dst=pg1.local_mac) / IP(src=local_host, dst=remote_host) / TCP(sport=local_sport, dport=remote_dport, flags="FA", seq=300, ack=101) ) diff --git a/test/test_neighbor.py b/test/test_neighbor.py index 6fcf13f8261..d11d4abac14 100644 --- a/test/test_neighbor.py +++ b/test/test_neighbor.py @@ -2176,6 +2176,7 @@ class ARPTestCase(VppTestCase): table_id=1, ).add_vpp_config() + p2.dst = self.pg3.local_mac rxs = self.send_and_expect(self.pg3, [p2], self.pg1) for rx in rxs: self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.2", "10.0.1.128") diff --git a/test/test_pcap.py b/test/test_pcap.py index ae3a298f76a..72d215cea06 100644 --- a/test/test_pcap.py +++ b/test/test_pcap.py @@ -135,7 +135,7 @@ class TestPcap(VppTestCase): self.vapi.cli("classify filter pcap del mask l3 ip4 src") pkt = ( - Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) # wrong destination address / IP(src=self.pg0.local_ip4, dst=self.pg0.local_ip4, ttl=2) / UDP(sport=1234, dport=2345) diff --git a/test/test_reassembly.py b/test/test_reassembly.py index 6b6745f221c..98c50f9bb61 100644 --- a/test/test_reassembly.py +++ b/test/test_reassembly.py @@ -1647,7 +1647,7 @@ class TestIPv6Reassembly(VppTestCase): def test_atomic_fragment(self): """IPv6 atomic fragment""" pkt = ( - Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6, nh=44, plen=65535) / IPv6ExtHdrFragment( offset=8191, m=1, res1=0xFF, res2=0xFF, nh=255, id=0xFFFF @@ -1669,7 +1669,7 @@ class TestIPv6Reassembly(VppTestCase): self.send_and_assert_no_replies(self.pg0, [pkt]) pkt = ( - Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) / ICMPv6EchoRequest() ) @@ -1678,7 +1678,7 @@ class TestIPv6Reassembly(VppTestCase): def test_one_fragment(self): """whole packet in one fragment processed independently""" pkt = ( - Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) / ICMPv6EchoRequest() / Raw("X" * 1600) @@ -1690,7 +1690,7 @@ class TestIPv6Reassembly(VppTestCase): # send an atomic fragment with same id - should be reassembled pkt = ( - Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) / IPv6ExtHdrFragment(id=1) / ICMPv6EchoRequest() @@ -1705,7 +1705,7 @@ class TestIPv6Reassembly(VppTestCase): def test_bunch_of_fragments(self): """valid fragments followed by rogue fragments and atomic fragment""" pkt = ( - Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) / ICMPv6EchoRequest() / Raw("X" * 1600) @@ -1723,7 +1723,7 @@ class TestIPv6Reassembly(VppTestCase): self.send_and_assert_no_replies(self.pg0, inc_frag * 604) pkt = ( - Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) / IPv6ExtHdrFragment(id=1) / ICMPv6EchoRequest() @@ -1738,7 +1738,7 @@ class TestIPv6Reassembly(VppTestCase): ) self.vapi.ip_local_reass_enable_disable(enable_ip6=True) pkt = ( - Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) + Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac) / IPv6(src=self.src_if.remote_ip6, dst=self.src_if.local_ip6) / ICMPv6EchoRequest(id=1234) / Raw("X" * 1600) @@ -2184,7 +2184,7 @@ class TestIPv6SVReassembly(VppTestCase): def test_one_fragment(self): """whole packet in one fragment processed independently""" pkt = ( - Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) + Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac) / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) / ICMPv6EchoRequest() / Raw("X" * 1600) @@ -2196,7 +2196,7 @@ class TestIPv6SVReassembly(VppTestCase): # send an atomic fragment with same id - should be reassembled pkt = ( - Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) + Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac) / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) / IPv6ExtHdrFragment(id=1) / ICMPv6EchoRequest() @@ -2209,7 +2209,7 @@ class TestIPv6SVReassembly(VppTestCase): def test_bunch_of_fragments(self): """valid fragments followed by rogue fragments and atomic fragment""" pkt = ( - Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) + Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac) / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) / ICMPv6EchoRequest() / Raw("X" * 1600) @@ -2218,7 +2218,7 @@ class TestIPv6SVReassembly(VppTestCase): rx = self.send_and_expect(self.src_if, frags, self.dst_if) rogue = ( - Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) + Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac) / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) / IPv6ExtHdrFragment(id=1, nh=58, offset=608) / Raw("X" * 308) @@ -2227,7 +2227,7 @@ class TestIPv6SVReassembly(VppTestCase): self.send_and_expect(self.src_if, rogue * 604, self.dst_if) pkt = ( - Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) + Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac) / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) / IPv6ExtHdrFragment(id=1) / ICMPv6EchoRequest() diff --git a/test/test_srv6_mobile.py b/test/test_srv6_mobile.py index 9d39f194015..314dfc114e2 100644 --- a/test/test_srv6_mobile.py +++ b/test/test_srv6_mobile.py @@ -60,7 +60,7 @@ class TestSRv6EndMGTP4E(VppTestCase): pkts = list() for d, s in inner: pkt = ( - Ether() + Ether(dst=self.pg0.local_mac) / IPv6(dst=str(ip6_dst), src=str(ip6_src)) / IPv6ExtHdrSegmentRouting() / IPv6(dst=d, src=s) @@ -149,7 +149,7 @@ class TestSRv6TMGTP4D(VppTestCase): pkts = list() for d, s in inner: pkt = ( - Ether() + Ether(dst=self.pg0.local_mac) / IP(dst=str(ip4_dst), src=str(ip4_src)) / UDP(sport=2152, dport=2152) / GTP_U_Header(gtp_type="g_pdu", teid=200) @@ -254,7 +254,7 @@ class TestSRv6EndMGTP6E(VppTestCase): pkts = list() for d, s in inner: pkt = ( - Ether() + Ether(dst=self.pg0.local_mac) / IPv6(dst=str(ip6_dst), src=str(ip6_src)) / IPv6ExtHdrSegmentRouting( segleft=1, lastentry=0, tag=0, addresses=["a1::1"] @@ -344,7 +344,7 @@ class TestSRv6EndMGTP6D(VppTestCase): pkts = list() for d, s in inner: pkt = ( - Ether() + Ether(dst=self.pg0.local_mac) / IPv6(dst=str(ip6_dst), src=str(ip6_src)) / UDP(sport=2152, dport=2152) / GTP_U_Header(gtp_type="g_pdu", teid=200) diff --git a/test/test_trace_filter.py b/test/test_trace_filter.py index 58494cd7ad9..c188631c200 100644 --- a/test/test_trace_filter.py +++ b/test/test_trace_filter.py @@ -386,7 +386,7 @@ class TestTraceFilterInner(TemplateTraceFilter): def __gen_encrypt_pkt(self, scapy_sa, pkt): return Ether( - src=self.pg0.local_mac, dst=self.pg0.remote_mac + src=self.pg0.remote_mac, dst=self.pg0.local_mac ) / scapy_sa.encrypt(pkt) def test_encrypted_encap(self): diff --git a/test/test_vxlan.py b/test/test_vxlan.py index 6128d1070d3..284e1359116 100644 --- a/test/test_vxlan.py +++ b/test/test_vxlan.py @@ -580,6 +580,7 @@ class TestVxlanL2Mode(VppTestCase): # Send packets NUM_PKTS = 128 + p.dst = self.pg1.local_mac rx = self.send_and_expect(self.pg1, p * NUM_PKTS, self.pg0) self.assertEqual(NUM_PKTS, len(rx)) |